public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/6] Move throw_perror_with_name to common/
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
  2015-03-06 19:58 ` [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp Pedro Alves
@ 2015-03-06 19:58 ` Pedro Alves
  2015-03-06 19:58 ` [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint Pedro Alves
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

I have a use for throw_perror_with_name in nat/, but currently, only
perror_with_name is available to common gdb+gdbserver code.  This
patch makes throw_perror_with_name the function that client need to
implement (instead of perror_with_name), and adds a common
perror_with_name as a wrapper over throw_perror_with_name, just like
GDB's is currently.

gdb/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* utils.c (perror_with_name): Move ...
	* common/errors.c (perror_with_name): ... here.
	* common/errors.h: Include common-exceptions.h.
	(throw_perror_with_name): Declare.
	(perror_with_name): Update comment.

gdb/gdbserver/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* utils.c (perror_with_name): Rename to ...
	(throw_perror_with_name): ... this.  Use throw_error in gdbserver.
---
 gdb/common/errors.c   |  8 ++++++++
 gdb/common/errors.h   | 13 +++++++++++--
 gdb/gdbserver/utils.c |  6 +++++-
 gdb/utils.c           |  8 --------
 4 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/gdb/common/errors.c b/gdb/common/errors.c
index 00b325e..5fd4a80 100644
--- a/gdb/common/errors.c
+++ b/gdb/common/errors.c
@@ -67,3 +67,11 @@ internal_warning (const char *file, int line, const char *fmt, ...)
   internal_vwarning (file, line, fmt, ap);
   va_end (ap);
 }
+
+/* See common/errors.h.  */
+
+void
+perror_with_name (const char *string)
+{
+  throw_perror_with_name (GENERIC_ERROR, string);
+}
diff --git a/gdb/common/errors.h b/gdb/common/errors.h
index 36e2c49..c7d85f1 100644
--- a/gdb/common/errors.h
+++ b/gdb/common/errors.h
@@ -20,6 +20,8 @@
 #ifndef COMMON_ERRORS_H
 #define COMMON_ERRORS_H
 
+#include "common-exceptions.h"
+
 /* A problem was detected, but the requested operation can still
    proceed.  A warning message is constructed using a printf- or
    vprintf-style argument list.  The function "vwarning" must be
@@ -76,8 +78,15 @@ extern void internal_vwarning (const char *file, int line,
 \f
 
 /* Like "error", but the error message is constructed by combining
-   STRING with the system error message for errno.  This function does
-   not return.  This function must be provided by the client.  */
+   STRING with the system error message for errno.  Uses ERRCODE for
+   the thrown exception.  This function does not return.  This
+   function must be provided by the client.  */
+
+extern void throw_perror_with_name (enum errors errcode, const char *string)
+  ATTRIBUTE_NORETURN;
+
+/* Convenience wrapper for "throw_perror_with_name" that throws
+   GENERIC_ERROR.  */
 
 extern void perror_with_name (const char *string) ATTRIBUTE_NORETURN;
 
diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c
index e89a862..cd5734b 100644
--- a/gdb/gdbserver/utils.c
+++ b/gdb/gdbserver/utils.c
@@ -54,7 +54,7 @@ xstrdup (const char *s)
    Then return to command level.  */
 
 void
-perror_with_name (const char *string)
+throw_perror_with_name (enum errors errcode, const char *string)
 {
   const char *err;
   char *combined;
@@ -68,7 +68,11 @@ perror_with_name (const char *string)
   strcat (combined, ": ");
   strcat (combined, err);
 
+#ifdef IN_PROCESS_AGENT
   error ("%s.", combined);
+#else
+  throw_error (errcode, "%s.", combined);
+#endif
 }
 
 /* Print an error message and return to top level.  */
diff --git a/gdb/utils.c b/gdb/utils.c
index 7172bba..6b1aa89 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -990,14 +990,6 @@ throw_perror_with_name (enum errors errcode, const char *string)
   throw_error (errcode, _("%s."), combined);
 }
 
-/* See throw_perror_with_name, ERRCODE defaults here to GENERIC_ERROR.  */
-
-void
-perror_with_name (const char *string)
-{
-  throw_perror_with_name (GENERIC_ERROR, string);
-}
-
 /* Same as perror_with_name except that it prints a warning instead
    of throwing an error.  */
 
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
@ 2015-03-06 19:58 ` Pedro Alves
  2015-03-19 17:39   ` Pedro Alves
  2015-03-06 19:58 ` [PATCH 1/6] Move throw_perror_with_name to common/ Pedro Alves
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

On GNU/Linux, this test sometimes FAILs like this:

 (gdb) run
 Starting program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/killed
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib64/libthread_db.so.1".
 ptrace: No such process.
 (gdb)
 Program terminated with signal SIGKILL, Killed.
 The program no longer exists.
 FAIL: gdb.threads/killed.exp: run program to completion (timeout)

Note the suspicious "No such process" line (that's errno==ESRCH).
Adding debug output we see:

  linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Stopped (signal) (stopped)
  LNW: waitpid(-1, ...) returned 18461, ERRNO-OK
  LLW: waitpid 18461 received Trace/breakpoint trap (stopped)
  LLW: Handling extended status 0x03057f
  LHEW: Got clone event from LWP 18461, new child is LWP 18465
  LNW: waitpid(-1, ...) returned 0, ERRNO-OK
  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0
  sigchld
  ptrace: No such process.
  (gdb) linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Killed (terminated)
  LLW: LWP 18465 exited.
  LNW: waitpid(-1, ...) returned 18461, No child processes
  LLW: waitpid 18461 received Killed (terminated)
  Process 18461 exited
  LNW: waitpid(-1, ...) returned -1, No child processes
  LLW: exit
  sigchld
  infrun: target_wait (-1, status) =
  infrun:   18461 [process 18461],
  infrun:   status->kind = signalled, signal = GDB_SIGNAL_KILL
  infrun: TARGET_WAITKIND_SIGNALLED

  Program terminated with signal SIGKILL, Killed.
  The program no longer exists.
  infrun: stop_waiting
  FAIL: gdb.threads/killed.exp: run program to completion (timeout)

The ptrace call that fails was a PTRACE_CONTINUE.

The issue is that here:

  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0

The first line shows we had just resumed LWP 18465, which does:

 void *
 child_func (void *dummy)
 {
   kill (pid, SIGKILL);
   exit (1);
 }

So if the kernel manages to schedule that thread fast enough, the
process may be killed before GDB has a chance to resume LWP 18461.

GDBserver has code at the tail end of linux_resume_one_lwp to cope
with this:

~~~
    ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
	    (PTRACE_TYPE_ARG3) 0,
	    /* Coerce to a uintptr_t first to avoid potential gcc warning
	       of coercing an 8 byte integer to a 4 byte pointer.  */
	    (PTRACE_TYPE_ARG4) (uintptr_t) signal);

    current_thread = saved_thread;
    if (errno)
      {
	/* ESRCH from ptrace either means that the thread was already
	   running (an error) or that it is gone (a race condition).  If
	   it's gone, we will get a notification the next time we wait,
	   so we can ignore the error.  We could differentiate these
	   two, but it's tricky without waiting; the thread still exists
	   as a zombie, so sending it signal 0 would succeed.  So just
	   ignore ESRCH.  */
	if (errno == ESRCH)
	  return;

	perror_with_name ("ptrace");
      }
~~~

However, that's not a complete fix, because between starting to handle
the resume request and getting that PTRACE_CONTINUE, we run other
ptrace calls that can also fail with ESRCH, and that end up throwing
an error.

So instead of ignoring ESRCH locally at each ptrace call, let ptrace
errors throw a new THREAD_NOT_FOUND_ERROR, and wrap the resume request
in a TRY_CATCH that swallows it.

For now, nothing in the core ever sees or handles this error, but I
envision that we'll find uses for it there too.

gdb/gdbserver/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call throw_ptrace_error.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows THREAD_NOT_FOUND_ERROR.

gdb/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call throw_ptrace_error.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows THREAD_NOT_FOUND_ERROR.
---
 gdb/common/common-exceptions.h |  3 +++
 gdb/gdbserver/linux-low.c      | 54 +++++++++++++++++++++++++++++-------------
 gdb/linux-nat.c                | 32 ++++++++++++++++++++++++-
 gdb/nat/ptrace-utils.c         | 22 ++++++++++++++++-
 4 files changed, 93 insertions(+), 18 deletions(-)

diff --git a/gdb/common/common-exceptions.h b/gdb/common/common-exceptions.h
index a32e6f9..1a13f18 100644
--- a/gdb/common/common-exceptions.h
+++ b/gdb/common/common-exceptions.h
@@ -93,6 +93,9 @@ enum errors {
      aborted as the inferior state is no longer valid.  */
   TARGET_CLOSE_ERROR,
 
+  /* The thread is gone.  */
+  THREAD_NOT_FOUND_ERROR,
+
   /* An undefined command was executed.  */
   UNDEFINED_COMMAND_ERROR,
 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 48d905b..f9c4079 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -28,6 +28,7 @@
 #include "nat/linux-ptrace.h"
 #include "nat/linux-procfs.h"
 #include "nat/linux-personality.h"
+#include "nat/ptrace-utils.h"
 #include <signal.h>
 #include <sys/ioctl.h>
 #include <fcntl.h>
@@ -3378,13 +3379,12 @@ stop_all_lwps (int suspend, struct lwp_info *except)
     }
 }
 
-/* Resume execution of the inferior process.
-   If STEP is nonzero, single-step it.
-   If SIGNAL is nonzero, give it that signal.  */
+/* Like linux_resume_one_lwp but throws THREAD_NOT_FOUND_ERROR if the
+   LWP has disappeared (ptrace returns ESRCH).  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lwp,
-		      int step, int signal, siginfo_t *info)
+linux_resume_one_lwp_throw (struct lwp_info *lwp,
+			    int step, int signal, siginfo_t *info)
 {
   struct thread_info *thread = get_lwp_thread (lwp);
   struct thread_info *saved_thread;
@@ -3576,18 +3576,40 @@ linux_resume_one_lwp (struct lwp_info *lwp,
 
   current_thread = saved_thread;
   if (errno)
-    {
-      /* ESRCH from ptrace either means that the thread was already
-	 running (an error) or that it is gone (a race condition).  If
-	 it's gone, we will get a notification the next time we wait,
-	 so we can ignore the error.  We could differentiate these
-	 two, but it's tricky without waiting; the thread still exists
-	 as a zombie, so sending it signal 0 would succeed.  So just
-	 ignore ESRCH.  */
-      if (errno == ESRCH)
-	return;
+    throw_ptrace_error ("resuming thread");
+}
 
-      perror_with_name ("ptrace");
+/* Resume execution of LWP.  If STEP is nonzero, single-step it.  If
+   SIGNAL is nonzero, give it that signal.  No error is throw if the
+   LWP disappears while we try to resume it, no error is thrown.  */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lwp,
+		      int step, int signal, siginfo_t *info)
+{
+  struct gdb_exception ex;
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      linux_resume_one_lwp_throw (lwp, step, signal, info);
+    }
+  if (ex.reason < 0)
+    {
+      if (ex.error == THREAD_NOT_FOUND_ERROR)
+	{
+	  /* We got an ESRCH while resuming the LWP.  ESRCH from
+	     ptrace either means that the thread was already running
+	     (an error/bug) or that it is gone (a race condition, e.g,
+	     another thread exited the whole process.), or it was
+	     killed by SIGKILL.  If it's gone, we will get a
+	     notification the next time we wait, so we can ignore the
+	     error.  We could differentiate these, but it's tricky
+	     without waiting; the thread still exists as a zombie, so
+	     sending it signal 0 would succeed.  So just ignore
+	     ESRCH.  */
+	}
+      else
+	throw_exception (ex);
     }
 }
 
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 20fe533..6bb62fd 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1504,7 +1504,7 @@ linux_nat_detach (struct target_ops *ops, const char *args, int from_tty)
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+linux_resume_one_lwp_throw (struct lwp_info *lp, int step, enum gdb_signal signo)
 {
   lp->step = step;
 
@@ -1528,6 +1528,36 @@ linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
   registers_changed_ptid (lp->ptid);
 }
 
+static void
+linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+{
+  struct gdb_exception ex;
+
+  TRY_CATCH (ex, RETURN_MASK_ERROR)
+    {
+      linux_resume_one_lwp_throw (lp, step, signo);
+    }
+  if (ex.reason < 0)
+    {
+      if (ex.error == THREAD_NOT_FOUND_ERROR)
+	{
+	  /* We got an ESRCH while resuming the LWP.  ESRCH from
+	     ptrace either means that the thread was already running
+	     (an error/bug) or that it is gone (a race condition, e.g,
+	     another thread exited the whole process.), or it was
+	     killed by SIGKILL.  If it's gone, we will get a
+	     notification the next time we wait, so we can ignore the
+	     error.  We could differentiate these, but it's tricky
+	     without waiting; the thread still exists as a zombie, so
+	     sending it signal 0 would succeed.  So just ignore
+	     ESRCH.  */
+	}
+      else
+	throw_exception (ex);
+    }
+}
+
+
 /* Resume LP.  */
 
 static void
diff --git a/gdb/nat/ptrace-utils.c b/gdb/nat/ptrace-utils.c
index a3dc9b5..ca59454 100644
--- a/gdb/nat/ptrace-utils.c
+++ b/gdb/nat/ptrace-utils.c
@@ -25,5 +25,25 @@
 void
 throw_ptrace_error (const char *string)
 {
-  perror_with_name (string);
+  enum errors errcode;
+
+  switch (errno)
+    {
+    case ESRCH:
+      /* This either means that:
+
+	 - the thread was already running (a GDB error/bug).
+
+	 - the thread is gone, a race condition.  E.g, another thread
+	   was set running and it exited the whole process, or the
+	   process was killed by a SIGKILL.
+
+	 Assume the latter.  */
+      errcode = THREAD_NOT_FOUND_ERROR;
+      break;
+    default:
+      errcode = GENERIC_ERROR;
+      break;
+    }
+  throw_perror_with_name (errcode, string);
 }
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 5/6] gdbserver/Linux: internal error when killing a process that is already gone
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
                   ` (3 preceding siblings ...)
  2015-03-06 19:58 ` [PATCH 2/6] Introduce throw_ptrace_error Pedro Alves
@ 2015-03-06 19:58 ` Pedro Alves
  2015-03-06 20:27 ` [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB Pedro Alves
  5 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

If the process disappears (e.g., killed with "kill -9" from the shell)
while it was stopped under GDBserver's control, and the GDBserver
tries to kill it, GDBserver asserts:

 (gdb) shell kill -9 23084
 (gdb) kill
 ...
 Killing process(es): 23084
 /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:972: A problem internal to GDBserver has been detected.
 kill_wait_lwp: Assertion `res > 0' failed.
 ...

gdb/gdbserver/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* linux-low.c (kill_wait_lwp): Don't assert if waitpid fails.
	Instead, ignore ECHILD, and throw an error for other errnos.
---
 gdb/gdbserver/linux-low.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index f9c4079..e536a51 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -970,7 +970,10 @@ kill_wait_lwp (struct lwp_info *lwp)
 	res = my_waitpid (lwpid, &wstat, __WCLONE);
     } while (res > 0 && WIFSTOPPED (wstat));
 
-  gdb_assert (res > 0);
+  /* Even if it was stopped, the child may have already disappeared.
+     E.g., if it was killed by SIGKILL.  */
+  if (res < 0 && errno != ECHILD)
+    perror_with_name ("kill_wait_lwp");
 }
 
 /* Callback for `find_inferior'.  Kills an lwp of a given process,
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
  2015-03-06 19:58 ` [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp Pedro Alves
  2015-03-06 19:58 ` [PATCH 1/6] Move throw_perror_with_name to common/ Pedro Alves
@ 2015-03-06 19:58 ` Pedro Alves
  2015-03-19 12:37   ` [pushed] native/Linux: internal error if resume is short-circuited (Re: [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint) Pedro Alves
  2015-03-06 19:58 ` [PATCH 2/6] Introduce throw_ptrace_error Pedro Alves
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

If the inferior disappears just after it was stopped at a breakpoint,
GDB internal errors on next resume:

 Executing on target: kill -9 11605    (timeout = 300)
 spawn -ignore SIGHUP kill -9 11605
 continue
 Continuing.
 /home/pedro/gdb/mygit/src/gdb/linux-nat.c:2590: internal-error: status_callback: Assertion `lp->status != 0' failed.

This is because the thread had stopped for a breakpoint, and had
already reported the event, so its ->status flag was cleared.  The
lwp's stopped, etc., flags should only be cleared when we're sure the
LWP was successfully resumed (see PR gdb/15713, git 8817a6f2).  So the
next resume hits an ESRCH error which throws before those flags are
cleared.  GDB core prints the error, and ends up calling target_wait
to poll remaining events.  We then trip on the assertion.

Fix this by bailing out earlier.  GDBserver is already doing this.

A follow up patch will add a test that exercises this
(gdb.base/killed-outside.exp).

gdb/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (status_callback): Return early if the LWP has no
	status pending.
---
 gdb/linux-nat.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 6bb62fd..68fd4bf 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2574,6 +2574,9 @@ status_callback (struct lwp_info *lp, void *data)
   if (!lp->resumed)
     return 0;
 
+  if (!lwp_status_pending_p (lp))
+    return 0;
+
   if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
       || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
     {
@@ -2582,8 +2585,6 @@ status_callback (struct lwp_info *lp, void *data)
       CORE_ADDR pc;
       int discard = 0;
 
-      gdb_assert (lp->status != 0);
-
       pc = regcache_read_pc (regcache);
 
       if (pc != lp->stop_pc)
@@ -2621,10 +2622,9 @@ status_callback (struct lwp_info *lp, void *data)
 	  linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
 	  return 0;
 	}
-      return 1;
     }
 
-  return lwp_status_pending_p (lp);
+  return 1;
 }
 
 /* Return non-zero if LP isn't stopped.  */
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/6] Fix problems if inferior disappears while being debugged
@ 2015-03-06 19:58 Pedro Alves
  2015-03-06 19:58 ` [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp Pedro Alves
                   ` (5 more replies)
  0 siblings, 6 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

The gdb.threads/killed.exp test has a race that happened to trigger
more often with my all-stop-non-stop series.

On GNU/Linux, this test sometimes FAILs like this:

 (gdb) run
 Starting program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/killed
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib64/libthread_db.so.1".
 ptrace: No such process.
 (gdb)
 Program terminated with signal SIGKILL, Killed.
 The program no longer exists.
 FAIL: gdb.threads/killed.exp: run program to completion (timeout)

This is ptrace erroring out with ESRCH (thread is gone).  We ignore
this error explicitly in some places (more in GDBserver than in GDB),
but not everywhere that it's needed.  And it's not clear that ignoring
is the best thing to do everywhere.  So I thought of a different
approach.

Currently, when ptrace errors, we usually call perror_with_name, which
just throws a generic error.  The series replaces the perror_with_name
calls with calls to a new throw_ptrace_error function that converts
ESRCH to a new error/exception (THREAD_NOT_FOUND_ERROR), which then
higher layers can catch and handle as appropriate.

I then wrote a test that explicitly kills the inferior while it is
being debugged, with "kill -9", and that uncovered even more
problems...  Both GDB and GDBserver internal error in some cases,
like this on native/Linux:

 src/gdb/linux-nat.c:2590: internal-error: status_callback: Assertion `lp->status != 0' failed.

and this on gdbserver/Linux:

 src/gdb/gdbserver/linux-low.c:972: A problem internal to GDBserver has been detected.
 kill_wait_lwp: Assertion `res > 0' failed.

This series also addresses those issues.

Pedro Alves (6):
  Move throw_perror_with_name to common/
  Introduce throw_ptrace_error
  Fix race exposed by gdb.threads/killed.exp
  native/Linux: internal error if inferior disappears after stopped by
    breakpoint
  gdbserver/Linux: internal error when killing a process that is already
    gone
  Add test that exercises the inferior being killed while stopped under
    GDB

 gdb/Makefile.in                           |   6 +-
 gdb/aarch64-linux-nat.c                   |  13 +--
 gdb/alphabsd-nat.c                        |  13 +--
 gdb/amd64-linux-nat.c                     |  19 ++---
 gdb/amd64bsd-nat.c                        |  19 ++---
 gdb/arm-linux-nat.c                       |   9 ++-
 gdb/common/common-exceptions.h            |   3 +
 gdb/common/errors.c                       |   8 ++
 gdb/common/errors.h                       |  13 ++-
 gdb/config/aarch64/linux.mh               |   2 +-
 gdb/config/alpha/alpha-linux.mh           |   2 +-
 gdb/config/alpha/fbsd.mh                  |   2 +-
 gdb/config/alpha/nbsd.mh                  |   2 +-
 gdb/config/arm/linux.mh                   |   2 +-
 gdb/config/arm/nbsdelf.mh                 |   2 +-
 gdb/config/i386/fbsd.mh                   |   2 +-
 gdb/config/i386/fbsd64.mh                 |   2 +-
 gdb/config/i386/linux.mh                  |   2 +-
 gdb/config/i386/linux64.mh                |   2 +-
 gdb/config/i386/nbsd64.mh                 |   2 +-
 gdb/config/i386/nbsdelf.mh                |   2 +-
 gdb/config/i386/obsd.mh                   |   2 +-
 gdb/config/i386/obsd64.mh                 |   2 +-
 gdb/config/ia64/linux.mh                  |   2 +-
 gdb/config/m32r/linux.mh                  |   2 +-
 gdb/config/m68k/linux.mh                  |   2 +-
 gdb/config/m68k/nbsdelf.mh                |   2 +-
 gdb/config/m68k/obsd.mh                   |   2 +-
 gdb/config/m88k/obsd.mh                   |   2 +-
 gdb/config/mips/linux.mh                  |   2 +-
 gdb/config/mips/nbsd.mh                   |   2 +-
 gdb/config/mips/obsd64.mh                 |   2 +-
 gdb/config/pa/hpux.mh                     |   2 +-
 gdb/config/pa/linux.mh                    |   2 +-
 gdb/config/pa/nbsd.mh                     |   2 +-
 gdb/config/pa/obsd.mh                     |   2 +-
 gdb/config/powerpc/aix.mh                 |   2 +-
 gdb/config/powerpc/fbsd.mh                |   2 +-
 gdb/config/powerpc/linux.mh               |   2 +-
 gdb/config/powerpc/nbsd.mh                |   2 +-
 gdb/config/powerpc/obsd.mh                |   2 +-
 gdb/config/powerpc/ppc64-linux.mh         |   2 +-
 gdb/config/powerpc/spu-linux.mh           |   2 +-
 gdb/config/s390/linux.mh                  |   2 +-
 gdb/config/sh/nbsd.mh                     |   2 +-
 gdb/config/sparc/fbsd.mh                  |   2 +-
 gdb/config/sparc/linux.mh                 |   2 +-
 gdb/config/sparc/linux64.mh               |   2 +-
 gdb/config/sparc/nbsd64.mh                |   2 +-
 gdb/config/sparc/nbsdelf.mh               |   2 +-
 gdb/config/sparc/obsd64.mh                |   2 +-
 gdb/config/tilegx/linux.mh                |   2 +-
 gdb/config/vax/nbsdelf.mh                 |   2 +-
 gdb/config/vax/obsd.mh                    |   2 +-
 gdb/config/xtensa/linux.mh                |   2 +-
 gdb/gdbserver/Makefile.in                 |   6 +-
 gdb/gdbserver/configure.srv               |   2 +-
 gdb/gdbserver/linux-low.c                 |  59 ++++++++++----
 gdb/gdbserver/utils.c                     |   6 +-
 gdb/hppanbsd-nat.c                        |  13 +--
 gdb/hppaobsd-nat.c                        |  13 +--
 gdb/i386-linux-nat.c                      |  27 ++++---
 gdb/i386bsd-nat.c                         |  23 +++---
 gdb/i386fbsd-nat.c                        |   3 +-
 gdb/inf-ptrace.c                          |  19 ++---
 gdb/linux-nat.c                           |  43 ++++++++--
 gdb/m32r-linux-nat.c                      |   7 +-
 gdb/m68kbsd-nat.c                         |  13 +--
 gdb/m68klinux-nat.c                       |  12 +--
 gdb/m88kbsd-nat.c                         |   7 +-
 gdb/mips-linux-nat.c                      |  19 ++---
 gdb/mips64obsd-nat.c                      |   7 +-
 gdb/mipsnbsd-nat.c                        |  13 +--
 gdb/nat/ptrace-utils.c                    |  49 +++++++++++
 gdb/nat/ptrace-utils.h                    |  28 +++++++
 gdb/obsd-nat.c                            |   9 ++-
 gdb/ppc-linux-nat.c                       |  63 ++++++++-------
 gdb/ppcfbsd-nat.c                         |  13 +--
 gdb/ppcnbsd-nat.c                         |  13 +--
 gdb/ppcobsd-nat.c                         |  13 +--
 gdb/rs6000-nat.c                          |   3 +-
 gdb/s390-linux-nat.c                      |  26 +++---
 gdb/shnbsd-nat.c                          |   7 +-
 gdb/sparc-nat.c                           |  15 ++--
 gdb/spu-linux-nat.c                       |   3 +-
 gdb/testsuite/gdb.base/killed-outside.c   |  34 ++++++++
 gdb/testsuite/gdb.base/killed-outside.exp | 130 ++++++++++++++++++++++++++++++
 gdb/tilegx-linux-nat.c                    |   6 +-
 gdb/utils.c                               |   8 --
 gdb/vaxbsd-nat.c                          |   7 +-
 gdb/x86-linux-nat.c                       |   9 ++-
 gdb/xtensa-linux-nat.c                    |  12 +--
 92 files changed, 642 insertions(+), 293 deletions(-)
 create mode 100644 gdb/nat/ptrace-utils.c
 create mode 100644 gdb/nat/ptrace-utils.h
 create mode 100644 gdb/testsuite/gdb.base/killed-outside.c
 create mode 100644 gdb/testsuite/gdb.base/killed-outside.exp

-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
                   ` (2 preceding siblings ...)
  2015-03-06 19:58 ` [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint Pedro Alves
@ 2015-03-06 19:58 ` Pedro Alves
  2015-03-06 21:04   ` Mark Kettenis
  2015-03-06 19:58 ` [PATCH 5/6] gdbserver/Linux: internal error when killing a process that is already gone Pedro Alves
  2015-03-06 20:27 ` [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB Pedro Alves
  5 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 19:58 UTC (permalink / raw)
  To: gdb-patches

This adds a new function that is meant to be called instead of
perror_with_name whenever we get an error out of ptrace.  The
idea is to convert some errno errors to different GDB exceptions in
a following patch.

gdb/
2015-03-06  Pedro Alves  <palves@redhat.com>

	* Makefile.in (HFILES_NO_SRCDIR): Add nat/ptrace-utils.h.
	(ptrace-utils.o): New rule.
	* aarch64-linux-nat.c: Include nat/ptrace-utils.h and use
	throw_ptrace error intead of perror_with_name.
	* alphabsd-nat.c, amd64-linux-nat.c, amd64bsd-nat.c,
	arm-linux-nat.c, hppabsd-nat.c, hppanbsd-nat.c, i386-linux-nat.c,
	i386bsd-nat.c, i386fbsd-nat.c, inf-ptrace.c, linux-nat.c,
	m32r-linux-nat.c, m68kbsd-nat.c, m68klinux-nat.c, m88kbsd-nat.c,
	mips-linux-nat.c, mips64obsd-nat.c, mipsnbsd-nat.c, obsd-nat.c,
	ppc-linux-nat.c, ppcfbsd-nat.c, ppcnbsd-nat.c, ppcobsd-nat.c,
	rs6000-nat.c, shnbsd-nat.c, sparc-nat.c, spu-linux-nat.c,
	tilegx-linux-nat.c, vaxbsd-nat.c, x86-linux-nat.c,
	xtensa-linux-nat.c.
	* nat/ptrace-utils.c: New file.
	* nat/ptrace-utils.h: New file.
	* config/aarch64/linux.mh, config/alpha/alpha-linux.mh,
	config/alpha/fbsd.mh, config/alpha/nbsd.mh, config/arm/linux.mh,
	config/i386/fbsd.mh, config/i386/fbsd64.mh, config/i386/linux.mh,
	config/i386/linux64.mh, config/i386/nbsdelf.mh,
	config/i386/obsd.mh, config/i386/obsd64.mh, config/ia64/linux.mh,
	config/m32r/linux.mh, config/m68k/linux.mh,
	config/m68k/nbsdelf.mh, config/m68k/obsd.mh, config/m88k/obsd.mh,
	config/mips/linux.mh, config/mips/nbsd.mh, config/mips/obsd64.mh,
	config/pa/hpux.mh, config/pa/linux.mh, config/pa/nbsd.mh,
	config/pa/obsd.mh, config/powerpc/fbsd.mh,
	config/powerpc/linux.mh, config/powerpc/nbsd.mh,
	config/powerpc/obsd.mh, config/powerpc/ppc64-linux.mh,
	config/powerpc/spu-linux.mh, config/s390/linux.mh,
	config/sh/nbsd.mh, config/sparc/fbsd.mh, config/sparc/linux.mh,
	config/sparc/linux64.mh, config/sparc/nbsd64.mh,
	config/sparc/nbsdelf.mh, config/sparc/obsd64.mh,
	config/tilegx/linux.mh, config/vax/nbsdelf.mh, config/vax/obsd.mh,
	config/xtensa/linux.mh: Add ptrace-utils.o.

gdb/gdbserver/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* Makefile.in (SFILES): Add nat/ptrace-utils.c.
	(ptrace-utils.o): New rule.
	* configure.srv (srv_linux_obj): Add ptrace-utils.o.
---
 gdb/Makefile.in                   |  6 +++-
 gdb/aarch64-linux-nat.c           | 13 ++++----
 gdb/alphabsd-nat.c                | 13 ++++----
 gdb/amd64-linux-nat.c             | 19 ++++++------
 gdb/amd64bsd-nat.c                | 19 ++++++------
 gdb/arm-linux-nat.c               |  9 +++---
 gdb/config/aarch64/linux.mh       |  2 +-
 gdb/config/alpha/alpha-linux.mh   |  2 +-
 gdb/config/alpha/fbsd.mh          |  2 +-
 gdb/config/alpha/nbsd.mh          |  2 +-
 gdb/config/arm/linux.mh           |  2 +-
 gdb/config/arm/nbsdelf.mh         |  2 +-
 gdb/config/i386/fbsd.mh           |  2 +-
 gdb/config/i386/fbsd64.mh         |  2 +-
 gdb/config/i386/linux.mh          |  2 +-
 gdb/config/i386/linux64.mh        |  2 +-
 gdb/config/i386/nbsd64.mh         |  2 +-
 gdb/config/i386/nbsdelf.mh        |  2 +-
 gdb/config/i386/obsd.mh           |  2 +-
 gdb/config/i386/obsd64.mh         |  2 +-
 gdb/config/ia64/linux.mh          |  2 +-
 gdb/config/m32r/linux.mh          |  2 +-
 gdb/config/m68k/linux.mh          |  2 +-
 gdb/config/m68k/nbsdelf.mh        |  2 +-
 gdb/config/m68k/obsd.mh           |  2 +-
 gdb/config/m88k/obsd.mh           |  2 +-
 gdb/config/mips/linux.mh          |  2 +-
 gdb/config/mips/nbsd.mh           |  2 +-
 gdb/config/mips/obsd64.mh         |  2 +-
 gdb/config/pa/hpux.mh             |  2 +-
 gdb/config/pa/linux.mh            |  2 +-
 gdb/config/pa/nbsd.mh             |  2 +-
 gdb/config/pa/obsd.mh             |  2 +-
 gdb/config/powerpc/aix.mh         |  2 +-
 gdb/config/powerpc/fbsd.mh        |  2 +-
 gdb/config/powerpc/linux.mh       |  2 +-
 gdb/config/powerpc/nbsd.mh        |  2 +-
 gdb/config/powerpc/obsd.mh        |  2 +-
 gdb/config/powerpc/ppc64-linux.mh |  2 +-
 gdb/config/powerpc/spu-linux.mh   |  2 +-
 gdb/config/s390/linux.mh          |  2 +-
 gdb/config/sh/nbsd.mh             |  2 +-
 gdb/config/sparc/fbsd.mh          |  2 +-
 gdb/config/sparc/linux.mh         |  2 +-
 gdb/config/sparc/linux64.mh       |  2 +-
 gdb/config/sparc/nbsd64.mh        |  2 +-
 gdb/config/sparc/nbsdelf.mh       |  2 +-
 gdb/config/sparc/obsd64.mh        |  2 +-
 gdb/config/tilegx/linux.mh        |  2 +-
 gdb/config/vax/nbsdelf.mh         |  2 +-
 gdb/config/vax/obsd.mh            |  2 +-
 gdb/config/xtensa/linux.mh        |  2 +-
 gdb/gdbserver/Makefile.in         |  6 +++-
 gdb/gdbserver/configure.srv       |  2 +-
 gdb/hppanbsd-nat.c                | 13 ++++----
 gdb/hppaobsd-nat.c                | 13 ++++----
 gdb/i386-linux-nat.c              | 27 +++++++++--------
 gdb/i386bsd-nat.c                 | 23 +++++++-------
 gdb/i386fbsd-nat.c                |  3 +-
 gdb/inf-ptrace.c                  | 19 ++++++------
 gdb/linux-nat.c                   |  3 +-
 gdb/m32r-linux-nat.c              |  7 +++--
 gdb/m68kbsd-nat.c                 | 13 ++++----
 gdb/m68klinux-nat.c               | 12 ++++----
 gdb/m88kbsd-nat.c                 |  7 +++--
 gdb/mips-linux-nat.c              | 19 ++++++------
 gdb/mips64obsd-nat.c              |  7 +++--
 gdb/mipsnbsd-nat.c                | 13 ++++----
 gdb/nat/ptrace-utils.c            | 29 ++++++++++++++++++
 gdb/nat/ptrace-utils.h            | 28 +++++++++++++++++
 gdb/obsd-nat.c                    |  9 +++---
 gdb/ppc-linux-nat.c               | 63 ++++++++++++++++++++-------------------
 gdb/ppcfbsd-nat.c                 | 13 ++++----
 gdb/ppcnbsd-nat.c                 | 13 ++++----
 gdb/ppcobsd-nat.c                 | 13 ++++----
 gdb/rs6000-nat.c                  |  3 +-
 gdb/s390-linux-nat.c              | 26 ++++++++--------
 gdb/shnbsd-nat.c                  |  7 +++--
 gdb/sparc-nat.c                   | 15 +++++-----
 gdb/spu-linux-nat.c               |  3 +-
 gdb/tilegx-linux-nat.c            |  6 ++--
 gdb/vaxbsd-nat.c                  |  7 +++--
 gdb/x86-linux-nat.c               |  9 +++---
 gdb/xtensa-linux-nat.c            | 12 ++++----
 84 files changed, 354 insertions(+), 260 deletions(-)
 create mode 100644 gdb/nat/ptrace-utils.c
 create mode 100644 gdb/nat/ptrace-utils.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 8c2a4de..b1bf620 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -966,7 +966,7 @@ common/print-utils.h common/rsp-low.h nat/x86-dregs.h x86-linux-nat.h \
 i386-linux-nat.h common/common-defs.h common/errors.h common/common-types.h \
 common/common-debug.h common/cleanups.h common/gdb_setjmp.h \
 common/common-exceptions.h target/target.h common/symbol.h \
-common/common-regcache.h fbsd-tdep.h nat/linux-personality.h
+common/common-regcache.h fbsd-tdep.h nat/linux-personality.h nat/ptrace-utils.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
@@ -2295,6 +2295,10 @@ linux-personality.o: ${srcdir}/nat/linux-personality.c
 	$(COMPILE) $(srcdir)/nat/linux-personality.c
 	$(POSTCOMPILE)
 
+ptrace-utils.o: $(srcdir)/nat/ptrace-utils.c
+	$(COMPILE) $(srcdir)/nat/ptrace-utils.c
+	$(POSTCOMPILE)
+
 #
 # gdb/tui/ dependencies
 #
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index aae4853..523f5b8 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -24,6 +24,7 @@
 #include "gdbcore.h"
 #include "regcache.h"
 #include "linux-nat.h"
+#include "nat/ptrace-utils.h"
 #include "target-descriptions.h"
 #include "auxv.h"
 #include "gdbcmd.h"
@@ -469,7 +470,7 @@ fetch_gregs_from_thread (struct regcache *regcache)
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to fetch general registers."));
+    throw_ptrace_error (_("Unable to fetch general registers."));
 
   for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
     regcache_raw_supply (regcache, regno,
@@ -493,7 +494,7 @@ store_gregs_to_thread (const struct regcache *regcache)
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to fetch general registers."));
+    throw_ptrace_error (_("Unable to fetch general registers."));
 
   for (regno = AARCH64_X0_REGNUM; regno <= AARCH64_CPSR_REGNUM; regno++)
     if (REG_VALID == regcache_register_status (regcache, regno))
@@ -502,7 +503,7 @@ store_gregs_to_thread (const struct regcache *regcache)
 
   ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to store general registers."));
+    throw_ptrace_error (_("Unable to store general registers."));
 }
 
 /* Fill GDB's register array with the fp/simd register values
@@ -522,7 +523,7 @@ fetch_fpregs_from_thread (struct regcache *regcache)
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to fetch FP/SIMD registers."));
+    throw_ptrace_error (_("Unable to fetch FP/SIMD registers."));
 
   for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
     regcache_raw_supply (regcache, regno,
@@ -549,7 +550,7 @@ store_fpregs_to_thread (const struct regcache *regcache)
 
   ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to fetch FP/SIMD registers."));
+    throw_ptrace_error (_("Unable to fetch FP/SIMD registers."));
 
   for (regno = AARCH64_V0_REGNUM; regno <= AARCH64_V31_REGNUM; regno++)
     if (REG_VALID == regcache_register_status (regcache, regno))
@@ -563,7 +564,7 @@ store_fpregs_to_thread (const struct regcache *regcache)
 
   ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
   if (ret < 0)
-    perror_with_name (_("Unable to store FP/SIMD registers."));
+    throw_ptrace_error (_("Unable to store FP/SIMD registers."));
 }
 
 /* Implement the "to_fetch_register" target_ops method.  */
diff --git a/gdb/alphabsd-nat.c b/gdb/alphabsd-nat.c
index 37a5c8d..74757ab 100644
--- a/gdb/alphabsd-nat.c
+++ b/gdb/alphabsd-nat.c
@@ -24,6 +24,7 @@
 #include "alpha-tdep.h"
 #include "alphabsd-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 #include <sys/types.h>
 #include <sys/ptrace.h>
@@ -93,7 +94,7 @@ alphabsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &gregs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       alphabsd_supply_reg (regcache, (char *) &gregs, regno);
       if (regno != -1)
@@ -107,7 +108,7 @@ alphabsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       alphabsd_supply_fpreg (regcache, (char *) &fpregs, regno);
     }
@@ -125,13 +126,13 @@ alphabsd_store_inferior_registers (struct target_ops *ops,
       struct reg gregs;
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &gregs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       alphabsd_fill_reg (regcache, (char *) &gregs, regno);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &gregs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
 
       if (regno != -1)
 	return;
@@ -144,13 +145,13 @@ alphabsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       alphabsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 \f
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index 22a1359..e35a52e 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -31,6 +31,7 @@
 
 #include "amd64-nat.h"
 #include "linux-nat.h"
+#include "nat/ptrace-utils.h"
 #include "amd64-tdep.h"
 #include "amd64-linux-tdep.h"
 #include "i386-linux-tdep.h"
@@ -138,7 +139,7 @@ amd64_linux_fetch_inferior_registers (struct target_ops *ops,
       elf_gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       amd64_supply_native_gregset (regcache, &regs, -1);
       if (regnum != -1)
@@ -158,14 +159,14 @@ amd64_linux_fetch_inferior_registers (struct target_ops *ops,
 	  iov.iov_len = sizeof (xstateregs);
 	  if (ptrace (PTRACE_GETREGSET, tid,
 		      (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	    perror_with_name (_("Couldn't get extended state status"));
+	    throw_ptrace_error (_("Couldn't get extended state status"));
 
 	  amd64_supply_xsave (regcache, -1, xstateregs);
 	}
       else
 	{
 	  if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-	    perror_with_name (_("Couldn't get floating point status"));
+	    throw_ptrace_error (_("Couldn't get floating point status"));
 
 	  amd64_supply_fxsave (regcache, -1, &fpregs);
 	}
@@ -193,12 +194,12 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
       elf_gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       amd64_collect_native_gregset (regcache, &regs, regnum);
 
       if (ptrace (PTRACE_SETREGS, tid, 0, (long) &regs) < 0)
-	perror_with_name (_("Couldn't write registers"));
+	throw_ptrace_error (_("Couldn't write registers"));
 
       if (regnum != -1)
 	return;
@@ -217,23 +218,23 @@ amd64_linux_store_inferior_registers (struct target_ops *ops,
 	  iov.iov_len = sizeof (xstateregs);
 	  if (ptrace (PTRACE_GETREGSET, tid,
 		      (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	    perror_with_name (_("Couldn't get extended state status"));
+	    throw_ptrace_error (_("Couldn't get extended state status"));
 
 	  amd64_collect_xsave (regcache, regnum, xstateregs, 0);
 
 	  if (ptrace (PTRACE_SETREGSET, tid,
 		      (unsigned int) NT_X86_XSTATE, (long) &iov) < 0)
-	    perror_with_name (_("Couldn't write extended state status"));
+	    throw_ptrace_error (_("Couldn't write extended state status"));
 	}
       else
 	{
 	  if (ptrace (PTRACE_GETFPREGS, tid, 0, (long) &fpregs) < 0)
-	    perror_with_name (_("Couldn't get floating point status"));
+	    throw_ptrace_error (_("Couldn't get floating point status"));
 
 	  amd64_collect_fxsave (regcache, regnum, &fpregs);
 
 	  if (ptrace (PTRACE_SETFPREGS, tid, 0, (long) &fpregs) < 0)
-	    perror_with_name (_("Couldn't write floating point status"));
+	    throw_ptrace_error (_("Couldn't write floating point status"));
 	}
     }
 }
diff --git a/gdb/amd64bsd-nat.c b/gdb/amd64bsd-nat.c
index 31060a123..e28715c 100644
--- a/gdb/amd64bsd-nat.c
+++ b/gdb/amd64bsd-nat.c
@@ -33,6 +33,7 @@
 #include "amd64-nat.h"
 #include "amd64bsd-nat.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 \f
 
 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
@@ -50,7 +51,7 @@ amd64bsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       amd64_supply_native_gregset (regcache, &regs, -1);
       if (regnum != -1)
@@ -63,7 +64,7 @@ amd64bsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       amd64_supply_fxsave (regcache, -1, &fpregs);
     }
@@ -84,13 +85,13 @@ amd64bsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       amd64_collect_native_gregset (regcache, &regs, regnum);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	          (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
 
       if (regnum != -1)
 	return;
@@ -102,13 +103,13 @@ amd64bsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       amd64_collect_fxsave (regcache, regnum, &fpregs);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 
@@ -138,7 +139,7 @@ amd64bsd_dr_get (ptid_t ptid, int regnum)
 
   if (ptrace (PT_GETDBREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't read debug registers"));
+    throw_ptrace_error (_("Couldn't read debug registers"));
 
   return DBREG_DRX ((&dbregs), regnum);
 }
@@ -150,7 +151,7 @@ amd64bsd_dr_set (int regnum, unsigned long value)
 
   if (ptrace (PT_GETDBREGS, ptid_get_pid (inferior_ptid),
               (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't get debug registers"));
+    throw_ptrace_error (_("Couldn't get debug registers"));
 
   /* For some mysterious reason, some of the reserved bits in the
      debug control register get set.  Mask these off, otherwise the
@@ -161,7 +162,7 @@ amd64bsd_dr_set (int regnum, unsigned long value)
 
   if (ptrace (PT_SETDBREGS, ptid_get_pid (inferior_ptid),
               (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't write debug registers"));
+    throw_ptrace_error (_("Couldn't write debug registers"));
 }
 
 void
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index bb8358c..34bc2db 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -22,6 +22,7 @@
 #include "regcache.h"
 #include "target.h"
 #include "linux-nat.h"
+#include "nat/ptrace-utils.h"
 #include "target-descriptions.h"
 #include "auxv.h"
 #include "observer.h"
@@ -1347,12 +1348,12 @@ arm_linux_prepare_to_resume (struct lwp_info *lwp)
         if (arm_hwbp_control_is_enabled (bpts[i].control))
           if (ptrace (PTRACE_SETHBPREGS, pid,
               (PTRACE_TYPE_ARG3) ((i << 1) + 1), &bpts[i].address) < 0)
-            perror_with_name (_("Unexpected error setting breakpoint"));
+            throw_ptrace_error (_("Unexpected error setting breakpoint"));
 
         if (bpts[i].control != 0)
           if (ptrace (PTRACE_SETHBPREGS, pid,
               (PTRACE_TYPE_ARG3) ((i << 1) + 2), &bpts[i].control) < 0)
-            perror_with_name (_("Unexpected error setting breakpoint"));
+            throw_ptrace_error (_("Unexpected error setting breakpoint"));
 
         arm_lwp_info->bpts_changed[i] = 0;
       }
@@ -1364,12 +1365,12 @@ arm_linux_prepare_to_resume (struct lwp_info *lwp)
         if (arm_hwbp_control_is_enabled (wpts[i].control))
           if (ptrace (PTRACE_SETHBPREGS, pid,
               (PTRACE_TYPE_ARG3) -((i << 1) + 1), &wpts[i].address) < 0)
-            perror_with_name (_("Unexpected error setting watchpoint"));
+            throw_ptrace_error (_("Unexpected error setting watchpoint"));
 
         if (wpts[i].control != 0)
           if (ptrace (PTRACE_SETHBPREGS, pid,
               (PTRACE_TYPE_ARG3) -((i << 1) + 2), &wpts[i].control) < 0)
-            perror_with_name (_("Unexpected error setting watchpoint"));
+            throw_ptrace_error (_("Unexpected error setting watchpoint"));
 
         arm_lwp_info->wpts_changed[i] = 0;
       }
diff --git a/gdb/config/aarch64/linux.mh b/gdb/config/aarch64/linux.mh
index 7f96e4d..3b47df2 100644
--- a/gdb/config/aarch64/linux.mh
+++ b/gdb/config/aarch64/linux.mh
@@ -19,7 +19,7 @@
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o aarch64-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o aarch64-linux-nat.o \
 	proc-service.o linux-thread-db.o linux-nat.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-osdata.o linux-waitpid.o \
 	linux-personality.o
diff --git a/gdb/config/alpha/alpha-linux.mh b/gdb/config/alpha/alpha-linux.mh
index 2ea02a1..b2ac9b5 100644
--- a/gdb/config/alpha/alpha-linux.mh
+++ b/gdb/config/alpha/alpha-linux.mh
@@ -1,6 +1,6 @@
 # Host: Little-endian Alpha running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o alpha-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o alpha-linux-nat.o \
 	fork-child.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o linux-personality.o
diff --git a/gdb/config/alpha/fbsd.mh b/gdb/config/alpha/fbsd.mh
index 6d1fe29..3867ca6 100644
--- a/gdb/config/alpha/fbsd.mh
+++ b/gdb/config/alpha/fbsd.mh
@@ -1,5 +1,5 @@
 # Host: FreeBSD/alpha
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	fbsd-nat.o alphabsd-nat.o bsd-kvm.o \
 	core-regset.o
 
diff --git a/gdb/config/alpha/nbsd.mh b/gdb/config/alpha/nbsd.mh
index b4185a7..b4b160f 100644
--- a/gdb/config/alpha/nbsd.mh
+++ b/gdb/config/alpha/nbsd.mh
@@ -1,4 +1,4 @@
 # Host: NetBSD/alpha
-NATDEPFILES= fork-child.o inf-ptrace.o alphabsd-nat.o bsd-kvm.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o alphabsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/arm/linux.mh b/gdb/config/arm/linux.mh
index 549bf42..9304e5d 100644
--- a/gdb/config/arm/linux.mh
+++ b/gdb/config/arm/linux.mh
@@ -1,7 +1,7 @@
 # Host: ARM based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o arm-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o arm-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o linux-personality.o
diff --git a/gdb/config/arm/nbsdelf.mh b/gdb/config/arm/nbsdelf.mh
index 4efb8a3..c1f122a 100644
--- a/gdb/config/arm/nbsdelf.mh
+++ b/gdb/config/arm/nbsdelf.mh
@@ -1,2 +1,2 @@
 # Host: NetBSD/arm
-NATDEPFILES= fork-child.o inf-ptrace.o armnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o armnbsd-nat.o
diff --git a/gdb/config/i386/fbsd.mh b/gdb/config/i386/fbsd.mh
index e5bff3a..37e19cb 100644
--- a/gdb/config/i386/fbsd.mh
+++ b/gdb/config/i386/fbsd.mh
@@ -1,5 +1,5 @@
 # Host: FreeBSD/i386
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	fbsd-nat.o x86-nat.o x86-dregs.o i386bsd-nat.o i386fbsd-nat.o \
 	bsd-kvm.o
 NAT_FILE= nm-fbsd.h
diff --git a/gdb/config/i386/fbsd64.mh b/gdb/config/i386/fbsd64.mh
index 329c526..3b82c7f 100644
--- a/gdb/config/i386/fbsd64.mh
+++ b/gdb/config/i386/fbsd64.mh
@@ -1,5 +1,5 @@
 # Host: FreeBSD/amd64
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	fbsd-nat.o amd64-nat.o amd64bsd-nat.o amd64fbsd-nat.o \
 	bsd-kvm.o x86-nat.o x86-dregs.o
 HAVE_NATIVE_GCORE_HOST = 1
diff --git a/gdb/config/i386/linux.mh b/gdb/config/i386/linux.mh
index 33fb281..c941245 100644
--- a/gdb/config/i386/linux.mh
+++ b/gdb/config/i386/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel 386 running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	x86-nat.o x86-dregs.o i386-linux-nat.o x86-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
diff --git a/gdb/config/i386/linux64.mh b/gdb/config/i386/linux64.mh
index d52eb52..d5b45d0 100644
--- a/gdb/config/i386/linux64.mh
+++ b/gdb/config/i386/linux64.mh
@@ -1,5 +1,5 @@
 # Host: GNU/Linux x86-64
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	x86-nat.o x86-dregs.o amd64-nat.o amd64-linux-nat.o \
 	x86-linux-nat.o \
 	linux-nat.o linux-osdata.o \
diff --git a/gdb/config/i386/nbsd64.mh b/gdb/config/i386/nbsd64.mh
index 5de8cf5..369a32d 100644
--- a/gdb/config/i386/nbsd64.mh
+++ b/gdb/config/i386/nbsd64.mh
@@ -1,3 +1,3 @@
 # Host: NetBSD/amd64
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	nbsd-nat.o amd64-nat.o amd64bsd-nat.o amd64nbsd-nat.o
diff --git a/gdb/config/i386/nbsdelf.mh b/gdb/config/i386/nbsdelf.mh
index d27c842..087696d 100644
--- a/gdb/config/i386/nbsdelf.mh
+++ b/gdb/config/i386/nbsdelf.mh
@@ -1,5 +1,5 @@
 # Host: NetBSD/i386 ELF
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	nbsd-nat.o i386bsd-nat.o i386nbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/i386/obsd.mh b/gdb/config/i386/obsd.mh
index a9041f4..15383f1 100644
--- a/gdb/config/i386/obsd.mh
+++ b/gdb/config/i386/obsd.mh
@@ -1,5 +1,5 @@
 # Host: OpenBSD/i386 ELF
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o \
 	i386bsd-nat.o i386obsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/i386/obsd64.mh b/gdb/config/i386/obsd64.mh
index 386a582..abd06b9 100644
--- a/gdb/config/i386/obsd64.mh
+++ b/gdb/config/i386/obsd64.mh
@@ -1,5 +1,5 @@
 # Host: OpenBSD/amd64
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o \
 	amd64-nat.o amd64bsd-nat.o amd64obsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/ia64/linux.mh b/gdb/config/ia64/linux.mh
index 9dce22b..c502340 100644
--- a/gdb/config/ia64/linux.mh
+++ b/gdb/config/ia64/linux.mh
@@ -1,7 +1,7 @@
 # Host: Intel IA-64 running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	ia64-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
diff --git a/gdb/config/m32r/linux.mh b/gdb/config/m32r/linux.mh
index 6b810e6..6a9456f 100644
--- a/gdb/config/m32r/linux.mh
+++ b/gdb/config/m32r/linux.mh
@@ -1,7 +1,7 @@
 # Host: M32R based machine running GNU/Linux
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o				\
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o				\
 	m32r-linux-nat.o proc-service.o linux-thread-db.o	\
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o linux-personality.o
diff --git a/gdb/config/m68k/linux.mh b/gdb/config/m68k/linux.mh
index f3b3baa..8317666 100644
--- a/gdb/config/m68k/linux.mh
+++ b/gdb/config/m68k/linux.mh
@@ -1,7 +1,7 @@
 # Host: Motorola m68k running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	m68klinux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
diff --git a/gdb/config/m68k/nbsdelf.mh b/gdb/config/m68k/nbsdelf.mh
index f2d97a1..7d458a7 100644
--- a/gdb/config/m68k/nbsdelf.mh
+++ b/gdb/config/m68k/nbsdelf.mh
@@ -1,4 +1,4 @@
 # Host: NetBSD/m68k ELF
-NATDEPFILES= m68kbsd-nat.o bsd-kvm.o fork-child.o inf-ptrace.o
+NATDEPFILES= m68kbsd-nat.o bsd-kvm.o fork-child.o inf-ptrace.o ptrace-utils.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/m68k/obsd.mh b/gdb/config/m68k/obsd.mh
index 7930e3b..92c00d7 100644
--- a/gdb/config/m68k/obsd.mh
+++ b/gdb/config/m68k/obsd.mh
@@ -1,4 +1,4 @@
 # Host: OpenBSD/m68k
-NATDEPFILES= m68kbsd-nat.o bsd-kvm.o fork-child.o inf-ptrace.o
+NATDEPFILES= m68kbsd-nat.o bsd-kvm.o fork-child.o inf-ptrace.o ptrace-utils.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/m88k/obsd.mh b/gdb/config/m88k/obsd.mh
index 2978edf..a9c9365 100644
--- a/gdb/config/m88k/obsd.mh
+++ b/gdb/config/m88k/obsd.mh
@@ -1,2 +1,2 @@
 # Host: OpenBSD/m88k
-NATDEPFILES= fork-child.o inf-ptrace.o m88kbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o m88kbsd-nat.o
diff --git a/gdb/config/mips/linux.mh b/gdb/config/mips/linux.mh
index d6a802f..eabc470 100644
--- a/gdb/config/mips/linux.mh
+++ b/gdb/config/mips/linux.mh
@@ -1,6 +1,6 @@
 # Host: Linux/MIPS
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o mips-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o mips-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
diff --git a/gdb/config/mips/nbsd.mh b/gdb/config/mips/nbsd.mh
index b76df13..fe437dd 100644
--- a/gdb/config/mips/nbsd.mh
+++ b/gdb/config/mips/nbsd.mh
@@ -1,2 +1,2 @@
 # Host: NetBSD/mips
-NATDEPFILES= fork-child.o inf-ptrace.o mipsnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o mipsnbsd-nat.o
diff --git a/gdb/config/mips/obsd64.mh b/gdb/config/mips/obsd64.mh
index ed04f9e..e9b7a7f 100644
--- a/gdb/config/mips/obsd64.mh
+++ b/gdb/config/mips/obsd64.mh
@@ -1,2 +1,2 @@
 # Host: OpenBSD/mips64
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o mips64obsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o mips64obsd-nat.o
diff --git a/gdb/config/pa/hpux.mh b/gdb/config/pa/hpux.mh
index 3151120..ec61058 100644
--- a/gdb/config/pa/hpux.mh
+++ b/gdb/config/pa/hpux.mh
@@ -1,3 +1,3 @@
 # Host: PA-RISC HP-UX
-NATDEPFILES= fork-child.o inf-ptrace.o inf-ttrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o inf-ttrace.o \
 	hppa-hpux-nat.o
diff --git a/gdb/config/pa/linux.mh b/gdb/config/pa/linux.mh
index 9539b64..15c3752 100644
--- a/gdb/config/pa/linux.mh
+++ b/gdb/config/pa/linux.mh
@@ -1,6 +1,6 @@
 # Host: Hewlett-Packard PA-RISC machine, running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	hppa-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
diff --git a/gdb/config/pa/nbsd.mh b/gdb/config/pa/nbsd.mh
index 5cff698..ff34c78 100644
--- a/gdb/config/pa/nbsd.mh
+++ b/gdb/config/pa/nbsd.mh
@@ -1,2 +1,2 @@
 # Host: NetBSD/hppa
-NATDEPFILES= fork-child.o inf-ptrace.o nbsd-nat.o hppanbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o nbsd-nat.o hppanbsd-nat.o
diff --git a/gdb/config/pa/obsd.mh b/gdb/config/pa/obsd.mh
index 50a1192..8e025b8 100644
--- a/gdb/config/pa/obsd.mh
+++ b/gdb/config/pa/obsd.mh
@@ -1,2 +1,2 @@
 # Host: OpenBSD/hppa
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o hppaobsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o hppaobsd-nat.o
diff --git a/gdb/config/powerpc/aix.mh b/gdb/config/powerpc/aix.mh
index 141501d..52930df 100644
--- a/gdb/config/powerpc/aix.mh
+++ b/gdb/config/powerpc/aix.mh
@@ -1,7 +1,7 @@
 # Host: IBM PowerPC running AIX
 
 # aix-thread.o is not listed in NATDEPFILES as it is pulled in by configure.
-NATDEPFILES= fork-child.o inf-ptrace.o rs6000-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o rs6000-nat.o
 
 # When compiled with cc, for debugging, this argument should be passed.
 # We have no idea who our current compiler is though, so we skip it.
diff --git a/gdb/config/powerpc/fbsd.mh b/gdb/config/powerpc/fbsd.mh
index e4e7b47..1ed4f49 100644
--- a/gdb/config/powerpc/fbsd.mh
+++ b/gdb/config/powerpc/fbsd.mh
@@ -17,7 +17,7 @@
 #  You should have received a copy of the GNU General Public License
 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
-NATDEPFILES= fbsd-nat.o fork-child.o inf-ptrace.o ppcfbsd-nat.o bsd-kvm.o
+NATDEPFILES= fbsd-nat.o fork-child.o inf-ptrace.o ptrace-utils.o ppcfbsd-nat.o bsd-kvm.o
 HAVE_NATIVE_GCORE_HOST = 1
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/powerpc/linux.mh b/gdb/config/powerpc/linux.mh
index 76e62c0..44f4a91 100644
--- a/gdb/config/powerpc/linux.mh
+++ b/gdb/config/powerpc/linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o linux-personality.o
diff --git a/gdb/config/powerpc/nbsd.mh b/gdb/config/powerpc/nbsd.mh
index db0390c..edea9dd 100644
--- a/gdb/config/powerpc/nbsd.mh
+++ b/gdb/config/powerpc/nbsd.mh
@@ -1,4 +1,4 @@
 # Host: NetBSD/powerpc
-NATDEPFILES= fork-child.o inf-ptrace.o ppcnbsd-nat.o bsd-kvm.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o ppcnbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/powerpc/obsd.mh b/gdb/config/powerpc/obsd.mh
index 2af667a..f8892ba 100644
--- a/gdb/config/powerpc/obsd.mh
+++ b/gdb/config/powerpc/obsd.mh
@@ -1,4 +1,4 @@
 # Host: OpenBSD/powerpc
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o ppcobsd-nat.o bsd-kvm.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o ppcobsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/powerpc/ppc64-linux.mh b/gdb/config/powerpc/ppc64-linux.mh
index 7eb6507..80c80fd 100644
--- a/gdb/config/powerpc/ppc64-linux.mh
+++ b/gdb/config/powerpc/ppc64-linux.mh
@@ -3,7 +3,7 @@
 XM_CLIBS=
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	ppc-linux-nat.o proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o ppc-linux.o linux-personality.o
diff --git a/gdb/config/powerpc/spu-linux.mh b/gdb/config/powerpc/spu-linux.mh
index d44aeeb..38f1588 100644
--- a/gdb/config/powerpc/spu-linux.mh
+++ b/gdb/config/powerpc/spu-linux.mh
@@ -3,6 +3,6 @@
 # This implements a 'pseudo-native' GDB running on the
 # PPU side of the Cell BE and debugging the SPU side.
 
-NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o \
+NATDEPFILES = spu-linux-nat.o fork-child.o inf-ptrace.o ptrace-utils.o \
 	      linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o
 
diff --git a/gdb/config/s390/linux.mh b/gdb/config/s390/linux.mh
index e1ad899..11fd721 100644
--- a/gdb/config/s390/linux.mh
+++ b/gdb/config/s390/linux.mh
@@ -1,6 +1,6 @@
 # Host: S390, running Linux
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o s390-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o s390-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-personality.o \
diff --git a/gdb/config/sh/nbsd.mh b/gdb/config/sh/nbsd.mh
index d5e67cb..d97e699 100644
--- a/gdb/config/sh/nbsd.mh
+++ b/gdb/config/sh/nbsd.mh
@@ -1,2 +1,2 @@
 # Host: NetBSD/sh
-NATDEPFILES= fork-child.o inf-ptrace.o shnbsd-nat.o
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o shnbsd-nat.o
diff --git a/gdb/config/sparc/fbsd.mh b/gdb/config/sparc/fbsd.mh
index 2774efb..45579b4 100644
--- a/gdb/config/sparc/fbsd.mh
+++ b/gdb/config/sparc/fbsd.mh
@@ -1,5 +1,5 @@
 # Host: FreeBSD/sparc64
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	fbsd-nat.o sparc-nat.o sparc64-nat.o sparc64fbsd-nat.o \
 	bsd-kvm.o
 HAVE_NATIVE_GCORE_HOST = 1
diff --git a/gdb/config/sparc/linux.mh b/gdb/config/sparc/linux.mh
index bd7fc86..3b43eab 100644
--- a/gdb/config/sparc/linux.mh
+++ b/gdb/config/sparc/linux.mh
@@ -1,7 +1,7 @@
 # Host: GNU/Linux SPARC
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc-linux-nat.o \
-	fork-child.o inf-ptrace.o \
+	fork-child.o inf-ptrace.o ptrace-utils.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
diff --git a/gdb/config/sparc/linux64.mh b/gdb/config/sparc/linux64.mh
index 86f984f..2f4d163 100644
--- a/gdb/config/sparc/linux64.mh
+++ b/gdb/config/sparc/linux64.mh
@@ -1,7 +1,7 @@
 # Host: GNU/Linux UltraSPARC
 NAT_FILE= config/nm-linux.h
 NATDEPFILES= sparc-nat.o sparc64-nat.o sparc64-linux-nat.o \
-	fork-child.o inf-ptrace.o \
+	fork-child.o inf-ptrace.o ptrace-utils.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
 	linux-procfs.o linux-ptrace.o linux-waitpid.o \
diff --git a/gdb/config/sparc/nbsd64.mh b/gdb/config/sparc/nbsd64.mh
index 056b74d..5a9ea07 100644
--- a/gdb/config/sparc/nbsd64.mh
+++ b/gdb/config/sparc/nbsd64.mh
@@ -1,5 +1,5 @@
 # Host: NetBSD/sparc64
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	sparc64nbsd-nat.o sparc-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/sparc/nbsdelf.mh b/gdb/config/sparc/nbsdelf.mh
index 6bf898f..6ae92bf 100644
--- a/gdb/config/sparc/nbsdelf.mh
+++ b/gdb/config/sparc/nbsdelf.mh
@@ -1,5 +1,5 @@
 # Host: NetBSD/sparc ELF
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	sparc-nat.o sparcnbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/sparc/obsd64.mh b/gdb/config/sparc/obsd64.mh
index d15e34d..d82386c 100644
--- a/gdb/config/sparc/obsd64.mh
+++ b/gdb/config/sparc/obsd64.mh
@@ -1,5 +1,5 @@
 # Host: OpenBSD/sparc64
-NATDEPFILES= fork-child.o inf-ptrace.o obsd-nat.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o obsd-nat.o \
 	sparc64obsd-nat.o sparc-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/tilegx/linux.mh b/gdb/config/tilegx/linux.mh
index b5edcd4..cdc96b9 100644
--- a/gdb/config/tilegx/linux.mh
+++ b/gdb/config/tilegx/linux.mh
@@ -1,7 +1,7 @@
 # Host: Tilera TILE-Gx running GNU/Linux.
 
 NAT_FILE= config/nm-linux.h
-NATDEPFILES= inf-ptrace.o fork-child.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o \
 	tilegx-linux-nat.o \
 	proc-service.o linux-thread-db.o \
 	linux-nat.o linux-osdata.o linux-fork.o \
diff --git a/gdb/config/vax/nbsdelf.mh b/gdb/config/vax/nbsdelf.mh
index dd9441b..28f0276 100644
--- a/gdb/config/vax/nbsdelf.mh
+++ b/gdb/config/vax/nbsdelf.mh
@@ -1,5 +1,5 @@
 # Host: NetBSD/vax ELF
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	vaxbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/vax/obsd.mh b/gdb/config/vax/obsd.mh
index d4d444f..57e729e 100644
--- a/gdb/config/vax/obsd.mh
+++ b/gdb/config/vax/obsd.mh
@@ -1,5 +1,5 @@
 # Host: OpenBSD/vax
-NATDEPFILES= fork-child.o inf-ptrace.o \
+NATDEPFILES= fork-child.o inf-ptrace.o ptrace-utils.o \
 	vaxbsd-nat.o bsd-kvm.o
 
 LOADLIBES= -lkvm
diff --git a/gdb/config/xtensa/linux.mh b/gdb/config/xtensa/linux.mh
index b4e59b3..50779d8 100644
--- a/gdb/config/xtensa/linux.mh
+++ b/gdb/config/xtensa/linux.mh
@@ -2,7 +2,7 @@
 
 NAT_FILE= config/nm-linux.h
 
-NATDEPFILES= inf-ptrace.o fork-child.o xtensa-linux-nat.o \
+NATDEPFILES= inf-ptrace.o ptrace-utils.o fork-child.o xtensa-linux-nat.o \
 	linux-thread-db.o proc-service.o \
 	linux-nat.o linux-osdata.o linux-fork.o linux-procfs.o linux-ptrace.o \
 	linux-waitpid.o linux-personality.o
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index 6dddf26..f3ef383 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -179,7 +179,8 @@ SFILES=	$(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
 	$(srcdir)/common/rsp-low.c $(srcdir)/common/errors.c \
 	$(srcdir)/common/common-debug.c $(srcdir)/common/cleanups.c \
 	$(srcdir)/common/common-exceptions.c $(srcdir)/symbol.c \
-	$(srcdir)/common/btrace-common.c
+	$(srcdir)/common/btrace-common.c \
+	$(srcdir)/nat/ptrace-utils.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -605,6 +606,9 @@ linux-personality.o: ../nat/linux-personality.c
 btrace-common.o: ../common/btrace-common.c
 	$(COMPILE) $<
 	$(POSTCOMPILE)
+ptrace-utils.o: ../nat/ptrace-utils.c
+	$(COMPILE) $<
+	$(POSTCOMPILE)
 
 aarch64.c : $(srcdir)/../regformats/aarch64.dat $(regdat_sh)
 	$(SHELL) $(regdat_sh) $(srcdir)/../regformats/aarch64.dat aarch64.c
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 98dd0d9..69fe157 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -42,7 +42,7 @@ srv_amd64_linux_xmlfiles="i386/amd64-linux.xml i386/amd64-avx-linux.xml i386/amd
 
 # Linux object files.  This is so we don't have to repeat
 # these files over and over again.
-srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o"
+srv_linux_obj="linux-low.o linux-osdata.o linux-procfs.o linux-ptrace.o linux-waitpid.o linux-personality.o ptrace-utils.o"
 
 # Input is taken from the "${target}" variable.
 
diff --git a/gdb/hppanbsd-nat.c b/gdb/hppanbsd-nat.c
index 0c96b49..568de05 100644
--- a/gdb/hppanbsd-nat.c
+++ b/gdb/hppanbsd-nat.c
@@ -27,6 +27,7 @@
 
 #include "hppa-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 #include "nbsd-nat.h"
 
@@ -169,7 +170,7 @@ hppanbsd_fetch_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       hppanbsd_supply_gregset (regcache, &regs);
     }
@@ -180,7 +181,7 @@ hppanbsd_fetch_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       hppanbsd_supply_fpregset (regcache, &fpregs);
     }
@@ -199,13 +200,13 @@ hppanbsd_store_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       hppanbsd_collect_gregset (regcache, &regs, regnum);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	          (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
     }
 
   if (regnum == -1 || hppanbsd_fpregset_supplies_p (regnum))
@@ -214,13 +215,13 @@ hppanbsd_store_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       hppanbsd_collect_fpregset (regcache, &fpregs, regnum);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 
diff --git a/gdb/hppaobsd-nat.c b/gdb/hppaobsd-nat.c
index fe49b99..5728b6c 100644
--- a/gdb/hppaobsd-nat.c
+++ b/gdb/hppaobsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "hppa-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 #include "obsd-nat.h"
 
@@ -195,7 +196,7 @@ hppaobsd_fetch_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       hppaobsd_supply_gregset (regcache, &regs);
     }
@@ -206,7 +207,7 @@ hppaobsd_fetch_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       hppaobsd_supply_fpregset (regcache, &fpregs);
     }
@@ -225,13 +226,13 @@ hppaobsd_store_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       hppaobsd_collect_gregset (regcache, &regs, regnum);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	          (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
     }
 
   if (regnum == -1 || hppaobsd_fpregset_supplies_p (regnum))
@@ -240,13 +241,13 @@ hppaobsd_store_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       hppaobsd_collect_fpregset (regcache, &fpregs, regnum);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 
diff --git a/gdb/i386-linux-nat.c b/gdb/i386-linux-nat.c
index 8cb8c66..1a0dfc0 100644
--- a/gdb/i386-linux-nat.c
+++ b/gdb/i386-linux-nat.c
@@ -34,6 +34,7 @@
 #include "x86-xstate.h"
 
 #include "x86-linux-nat.h"
+#include "nat/ptrace-utils.h"
 
 /* The register sets used in GNU/Linux ELF core-dumps are identical to
    the register sets in `struct user' that is used for a.out
@@ -212,7 +213,7 @@ fetch_regs (struct regcache *regcache, int tid)
 	  return;
 	}
 
-      perror_with_name (_("Couldn't get registers"));
+      throw_ptrace_error (_("Couldn't get registers"));
     }
 
   supply_gregset (regcache, (const elf_gregset_t *) regs_p);
@@ -227,12 +228,12 @@ store_regs (const struct regcache *regcache, int tid, int regno)
   elf_gregset_t regs;
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regno);
   
   if (ptrace (PTRACE_SETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 
 #else
@@ -276,7 +277,7 @@ fetch_fpregs (struct regcache *regcache, int tid)
   elf_fpregset_t fpregs;
 
   if (ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   supply_fpregset (regcache, (const elf_fpregset_t *) &fpregs);
 }
@@ -290,12 +291,12 @@ store_fpregs (const struct regcache *regcache, int tid, int regno)
   elf_fpregset_t fpregs;
 
   if (ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   fill_fpregset (regcache, &fpregs, regno);
 
   if (ptrace (PTRACE_SETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't write floating point status"));
+    throw_ptrace_error (_("Couldn't write floating point status"));
 }
 
 #else
@@ -332,7 +333,7 @@ fetch_xstateregs (struct regcache *regcache, int tid)
   iov.iov_len = sizeof(xstateregs);
   if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
 	      &iov) < 0)
-    perror_with_name (_("Couldn't read extended state status"));
+    throw_ptrace_error (_("Couldn't read extended state status"));
 
   i387_supply_xsave (regcache, -1, xstateregs);
   return 1;
@@ -355,13 +356,13 @@ store_xstateregs (const struct regcache *regcache, int tid, int regno)
   iov.iov_len = sizeof(xstateregs);
   if (ptrace (PTRACE_GETREGSET, tid, (unsigned int) NT_X86_XSTATE,
 	      &iov) < 0)
-    perror_with_name (_("Couldn't read extended state status"));
+    throw_ptrace_error (_("Couldn't read extended state status"));
 
   i387_collect_xsave (regcache, regno, xstateregs, 0);
 
   if (ptrace (PTRACE_SETREGSET, tid, (unsigned int) NT_X86_XSTATE,
 	      (int) &iov) < 0)
-    perror_with_name (_("Couldn't write extended state status"));
+    throw_ptrace_error (_("Couldn't write extended state status"));
 
   return 1;
 }
@@ -388,7 +389,7 @@ fetch_fpxregs (struct regcache *regcache, int tid)
 	  return 0;
 	}
 
-      perror_with_name (_("Couldn't read floating-point and SSE registers"));
+      throw_ptrace_error (_("Couldn't read floating-point and SSE registers"));
     }
 
   i387_supply_fxsave (regcache, -1, (const elf_fpxregset_t *) &fpxregs);
@@ -415,13 +416,13 @@ store_fpxregs (const struct regcache *regcache, int tid, int regno)
 	  return 0;
 	}
 
-      perror_with_name (_("Couldn't read floating-point and SSE registers"));
+      throw_ptrace_error (_("Couldn't read floating-point and SSE registers"));
     }
 
   i387_collect_fxsave (regcache, regno, &fpxregs);
 
   if (ptrace (PTRACE_SETFPXREGS, tid, 0, &fpxregs) == -1)
-    perror_with_name (_("Couldn't write floating-point and SSE registers"));
+    throw_ptrace_error (_("Couldn't write floating-point and SSE registers"));
 
   return 1;
 }
@@ -709,7 +710,7 @@ i386_linux_resume (struct target_ops *ops,
     }
 
   if (ptrace (request, pid, 0, gdb_signal_to_host (signal)) == -1)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 }
 \f
 
diff --git a/gdb/i386bsd-nat.c b/gdb/i386bsd-nat.c
index 16e0707..44b24b0 100644
--- a/gdb/i386bsd-nat.c
+++ b/gdb/i386bsd-nat.c
@@ -31,6 +31,7 @@
 #include "i387-tdep.h"
 #include "i386bsd-nat.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 \f
 
 /* In older BSD versions we cannot get at some of the segment
@@ -136,7 +137,7 @@ i386bsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       i386bsd_supply_gregset (regcache, &regs);
       if (regnum != -1)
@@ -160,14 +161,14 @@ i386bsd_fetch_inferior_registers (struct target_ops *ops,
 	{
           if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		      (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	    perror_with_name (_("Couldn't get floating point status"));
+	    throw_ptrace_error (_("Couldn't get floating point status"));
 
 	  i387_supply_fsave (regcache, -1, &fpregs);
 	}
 #else
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       i387_supply_fsave (regcache, -1, &fpregs);
 #endif
@@ -187,13 +188,13 @@ i386bsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       i386bsd_collect_gregset (regcache, &regs, regnum);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	          (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
 
       if (regnum != -1)
 	return;
@@ -215,7 +216,7 @@ i386bsd_store_inferior_registers (struct target_ops *ops,
 
 	  if (ptrace (PT_SETXMMREGS, ptid_get_pid (inferior_ptid),
 		      (PTRACE_TYPE_ARG3) xmmregs, 0) == -1)
-            perror_with_name (_("Couldn't write XMM registers"));
+            throw_ptrace_error (_("Couldn't write XMM registers"));
 	}
       else
 	{
@@ -223,13 +224,13 @@ i386bsd_store_inferior_registers (struct target_ops *ops,
 #endif
           if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		      (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	    perror_with_name (_("Couldn't get floating point status"));
+	    throw_ptrace_error (_("Couldn't get floating point status"));
 
           i387_collect_fsave (regcache, regnum, &fpregs);
 
           if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		      (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	    perror_with_name (_("Couldn't write floating point status"));
+	    throw_ptrace_error (_("Couldn't write floating point status"));
 #ifdef HAVE_PT_GETXMMREGS
         }
 #endif
@@ -268,7 +269,7 @@ i386bsd_dr_get (ptid_t ptid, int regnum)
 
   if (ptrace (PT_GETDBREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't read debug registers"));
+    throw_ptrace_error (_("Couldn't read debug registers"));
 
   return DBREG_DRX ((&dbregs), regnum);
 }
@@ -280,7 +281,7 @@ i386bsd_dr_set (int regnum, unsigned int value)
 
   if (ptrace (PT_GETDBREGS, ptid_get_pid (inferior_ptid),
               (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't get debug registers"));
+    throw_ptrace_error (_("Couldn't get debug registers"));
 
   /* For some mysterious reason, some of the reserved bits in the
      debug control register get set.  Mask these off, otherwise the
@@ -291,7 +292,7 @@ i386bsd_dr_set (int regnum, unsigned int value)
 
   if (ptrace (PT_SETDBREGS, ptid_get_pid (inferior_ptid),
               (PTRACE_TYPE_ARG3) &dbregs, 0) == -1)
-    perror_with_name (_("Couldn't write debug registers"));
+    throw_ptrace_error (_("Couldn't write debug registers"));
 }
 
 void
diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
index ad439e3..ad37c13 100644
--- a/gdb/i386fbsd-nat.c
+++ b/gdb/i386fbsd-nat.c
@@ -31,6 +31,7 @@
 #include "i386-tdep.h"
 #include "x86-nat.h"
 #include "i386bsd-nat.h"
+#include "nat/ptrace-utils.h"
 
 /* Resume execution of the inferior process.  If STEP is nonzero,
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
@@ -77,7 +78,7 @@ i386fbsd_resume (struct target_ops *ops,
      written a new PC value to the child.)  */
   if (ptrace (request, pid, (caddr_t) 1,
 	      gdb_signal_to_host (signal)) == -1)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 }
 \f
 
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index cd58dfb..06382d5 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -29,6 +29,7 @@
 #include <signal.h>
 
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "inf-child.h"
 #include "gdbthread.h"
 
@@ -52,7 +53,7 @@ inf_ptrace_follow_fork (struct target_ops *ops, int follow_child,
 	 infrun.c.  */
 
       if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
-	perror_with_name (("ptrace"));
+	throw_ptrace_error (("ptrace"));
     }
 
   return 0;
@@ -130,7 +131,7 @@ inf_ptrace_post_startup_inferior (struct target_ops *self, ptid_t pid)
   pe.pe_set_event |= PTRACE_FORK;
   if (ptrace (PT_SET_EVENT_MASK, ptid_get_pid (pid),
 	      (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 }
 
 #endif
@@ -197,7 +198,7 @@ inf_ptrace_attach (struct target_ops *ops, const char *args, int from_tty)
   errno = 0;
   ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 #else
   error (_("This system does not support attaching to a process"));
 #endif
@@ -226,7 +227,7 @@ inf_ptrace_post_attach (struct target_ops *self, int pid)
   pe.pe_set_event |= PTRACE_FORK;
   if (ptrace (PT_SET_EVENT_MASK, pid,
 	      (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 }
 
 #endif
@@ -260,7 +261,7 @@ inf_ptrace_detach (struct target_ops *ops, const char *args, int from_tty)
   errno = 0;
   ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, sig);
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 #else
   error (_("This system does not support detaching from a process"));
 #endif
@@ -356,7 +357,7 @@ inf_ptrace_resume (struct target_ops *ops,
   errno = 0;
   ptrace (request, pid, (PTRACE_TYPE_ARG3)1, gdb_signal_to_host (signal));
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 }
 
 /* Wait for the child specified by PTID to do something.  Return the
@@ -409,7 +410,7 @@ inf_ptrace_wait (struct target_ops *ops,
 
       if (ptrace (PT_GET_PROCESS_STATE, pid,
 		  (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
-	perror_with_name (("ptrace"));
+	throw_ptrace_error (("ptrace"));
 
       switch (pe.pe_report_event)
 	{
@@ -420,11 +421,11 @@ inf_ptrace_wait (struct target_ops *ops,
 	  /* Make sure the other end of the fork is stopped too.  */
 	  fpid = waitpid (pe.pe_other_pid, &status, 0);
 	  if (fpid == -1)
-	    perror_with_name (("waitpid"));
+	    throw_ptrace_error (("waitpid"));
 
 	  if (ptrace (PT_GET_PROCESS_STATE, fpid,
 		      (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
-	    perror_with_name (("ptrace"));
+	    throw_ptrace_error (("ptrace"));
 
 	  gdb_assert (pe.pe_report_event == PTRACE_FORK);
 	  gdb_assert (pe.pe_other_pid == pid);
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 627280e..20fe533 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -40,6 +40,7 @@
 #include "regset.h"
 #include "inf-child.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "auxv.h"
 #include <sys/procfs.h>		/* for elf_gregset etc.  */
 #include "elf-bfd.h"		/* for elfcore_write_* */
@@ -425,7 +426,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child,
 	    {
 	      linux_disable_event_reporting (child_pid);
 	      if (ptrace (PTRACE_SINGLESTEP, child_pid, 0, 0) < 0)
-		perror_with_name (_("Couldn't do single step"));
+		throw_ptrace_error (_("Couldn't do single step"));
 	      if (my_waitpid (child_pid, &status, 0) < 0)
 		perror_with_name (_("Couldn't wait vfork process"));
 	    }
diff --git a/gdb/m32r-linux-nat.c b/gdb/m32r-linux-nat.c
index e5e867b..3ece241 100644
--- a/gdb/m32r-linux-nat.c
+++ b/gdb/m32r-linux-nat.c
@@ -22,6 +22,7 @@
 #include "gdbcore.h"
 #include "regcache.h"
 #include "linux-nat.h"
+#include "nat/ptrace-utils.h"
 #include "target.h"
 #include <sys/ptrace.h>
 #include <sys/user.h>
@@ -109,7 +110,7 @@ fetch_regs (struct regcache *regcache, int tid)
   elf_gregset_t regs;
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   supply_gregset (regcache, (const elf_gregset_t *) &regs);
 }
@@ -158,12 +159,12 @@ store_regs (const struct regcache *regcache, int tid, int regno)
   elf_gregset_t regs;
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regno);
 
   if (ptrace (PTRACE_SETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 \f
 
diff --git a/gdb/m68kbsd-nat.c b/gdb/m68kbsd-nat.c
index 0b7ea21..2126dfd 100644
--- a/gdb/m68kbsd-nat.c
+++ b/gdb/m68kbsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "m68k-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 static int
 m68kbsd_gregset_supplies_p (int regnum)
@@ -117,7 +118,7 @@ m68kbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       m68kbsd_supply_gregset (regcache, &regs);
     }
@@ -128,7 +129,7 @@ m68kbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       m68kbsd_supply_fpregset (regcache, &fpregs);
     }
@@ -147,13 +148,13 @@ m68kbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
                   (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       m68kbsd_collect_gregset (regcache, &regs, regnum);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	          (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't write registers"));
+        throw_ptrace_error (_("Couldn't write registers"));
     }
 
   if (regnum == -1 || m68kbsd_fpregset_supplies_p (regnum))
@@ -162,13 +163,13 @@ m68kbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       m68kbsd_collect_fpregset (regcache, &fpregs, regnum);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 \f
diff --git a/gdb/m68klinux-nat.c b/gdb/m68klinux-nat.c
index 8f89d7b..ee95ffd 100644
--- a/gdb/m68klinux-nat.c
+++ b/gdb/m68klinux-nat.c
@@ -265,7 +265,7 @@ fetch_regs (struct regcache *regcache, int tid)
 	  return;
 	}
 
-      perror_with_name (_("Couldn't get registers"));
+      throw_ptrace_error (_("Couldn't get registers"));
     }
 
   supply_gregset (regcache, (const elf_gregset_t *) &regs);
@@ -280,12 +280,12 @@ store_regs (const struct regcache *regcache, int tid, int regno)
   elf_gregset_t regs;
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regno);
 
   if (ptrace (PTRACE_SETREGS, tid, 0, (int) &regs) < 0)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 
 #else
@@ -362,7 +362,7 @@ fetch_fpregs (struct regcache *regcache, int tid)
   elf_fpregset_t fpregs;
 
   if (ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   supply_fpregset (regcache, (const elf_fpregset_t *) &fpregs);
 }
@@ -376,12 +376,12 @@ store_fpregs (const struct regcache *regcache, int tid, int regno)
   elf_fpregset_t fpregs;
 
   if (ptrace (PTRACE_GETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   fill_fpregset (regcache, &fpregs, regno);
 
   if (ptrace (PTRACE_SETFPREGS, tid, 0, (int) &fpregs) < 0)
-    perror_with_name (_("Couldn't write floating point status"));
+    throw_ptrace_error (_("Couldn't write floating point status"));
 }
 
 #else
diff --git a/gdb/m88kbsd-nat.c b/gdb/m88kbsd-nat.c
index 22c2a77..85ce83c 100644
--- a/gdb/m88kbsd-nat.c
+++ b/gdb/m88kbsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "m88k-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 /* Supply the general-purpose registers stored in GREGS to REGCACHE.  */
 
@@ -70,7 +71,7 @@ m88kbsd_fetch_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   m88kbsd_supply_gregset (regcache, &regs);
 }
@@ -86,13 +87,13 @@ m88kbsd_store_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   m88kbsd_collect_gregset (regcache, &regs, regnum);
 
   if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 \f
 
diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c
index a36bb63..74b2f98 100644
--- a/gdb/mips-linux-nat.c
+++ b/gdb/mips-linux-nat.c
@@ -36,6 +36,7 @@
 #include <asm/ptrace.h>
 
 #include "nat/mips-linux-watch.h"
+#include "nat/ptrace-utils.h"
 
 #include "features/mips-linux.c"
 #include "features/mips-dsp-linux.c"
@@ -259,7 +260,7 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 	      have_ptrace_regsets = 0;
 	      return;
 	    }
-	  perror_with_name (_("Couldn't get registers"));
+	  throw_ptrace_error (_("Couldn't get registers"));
 	}
 
       mips64_supply_gregset (regcache,
@@ -278,7 +279,7 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
 	      have_ptrace_regsets = 0;
 	      return;
 	    }
-	  perror_with_name (_("Couldn't get FP registers"));
+	  throw_ptrace_error (_("Couldn't get FP registers"));
 	}
 
       mips64_supply_fpregset (regcache,
@@ -341,12 +342,12 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
       mips64_elf_gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       mips64_fill_gregset (regcache, &regs, regno);
 
       if (ptrace (PTRACE_SETREGS, tid, 0L, (PTRACE_TYPE_ARG3) &regs) == -1)
-	perror_with_name (_("Couldn't set registers"));
+	throw_ptrace_error (_("Couldn't set registers"));
     }
 
   if (regno == -1 || is_fp)
@@ -355,13 +356,13 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
 
       if (ptrace (PTRACE_GETFPREGS, tid, 0L,
 		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	throw_ptrace_error (_("Couldn't get FP registers"));
 
       mips64_fill_fpregset (regcache, &fp_regs, regno);
 
       if (ptrace (PTRACE_SETFPREGS, tid, 0L,
 		  (PTRACE_TYPE_ARG3) &fp_regs) == -1)
-	perror_with_name (_("Couldn't set FP registers"));
+	throw_ptrace_error (_("Couldn't set FP registers"));
     }
 
   if (is_dsp)
@@ -446,7 +447,7 @@ mips_linux_read_description (struct target_ops *ops)
 	  have_dsp = 0;
 	  break;
 	default:
-	  perror_with_name (_("Couldn't check DSP support"));
+	  throw_ptrace_error (_("Couldn't check DSP support"));
 	  break;
 	}
     }
@@ -615,7 +616,7 @@ write_watchpoint_regs (void)
     {
       tid = ptid_get_lwp (lp->ptid);
       if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
-	perror_with_name (_("Couldn't write debug register"));
+	throw_ptrace_error (_("Couldn't write debug register"));
     }
   return 0;
 }
@@ -635,7 +636,7 @@ mips_linux_new_thread (struct lwp_info *lp)
 
   tid = ptid_get_lwp (lp->ptid);
   if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
-    perror_with_name (_("Couldn't write debug register"));
+    throw_ptrace_error (_("Couldn't write debug register"));
 }
 
 /* Target to_insert_watchpoint implementation.  Try to insert a new
diff --git a/gdb/mips64obsd-nat.c b/gdb/mips64obsd-nat.c
index 56d4fa9..e268edc 100644
--- a/gdb/mips64obsd-nat.c
+++ b/gdb/mips64obsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "mips-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "obsd-nat.h"
 
 /* Shorthand for some register numbers used below.  */
@@ -85,7 +86,7 @@ mips64obsd_fetch_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   mips64obsd_supply_gregset (regcache, &regs);
 }
@@ -101,13 +102,13 @@ mips64obsd_store_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   mips64obsd_collect_gregset (regcache, &regs, regnum);
 
   if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 \f
 
diff --git a/gdb/mipsnbsd-nat.c b/gdb/mipsnbsd-nat.c
index 34dcd87..3ff1e96 100644
--- a/gdb/mipsnbsd-nat.c
+++ b/gdb/mipsnbsd-nat.c
@@ -29,6 +29,7 @@
 #include "mips-tdep.h"
 #include "mipsnbsd-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 /* Determine if PT_GETREGS fetches this register.  */
 static int
@@ -49,7 +50,7 @@ mipsnbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
       
       mipsnbsd_supply_reg (regcache, (char *) &regs, regno);
       if (regno != -1)
@@ -63,7 +64,7 @@ mipsnbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       mipsnbsd_supply_fpreg (regcache, (char *) &fpregs, regno);
     }
@@ -80,13 +81,13 @@ mipsnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       mipsnbsd_fill_reg (regcache, (char *) &regs, regno);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid), 
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't write registers"));
+	throw_ptrace_error (_("Couldn't write registers"));
 
       if (regno != -1)
 	return;
@@ -99,13 +100,13 @@ mipsnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       mipsnbsd_fill_fpreg (regcache, (char *) &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 }
 \f
diff --git a/gdb/nat/ptrace-utils.c b/gdb/nat/ptrace-utils.c
new file mode 100644
index 0000000..a3dc9b5
--- /dev/null
+++ b/gdb/nat/ptrace-utils.c
@@ -0,0 +1,29 @@
+/* Misc ptrace utils.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   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 "common-defs.h"
+#include "ptrace-utils.h"
+
+/* See ptrace-utils.h.  */
+
+void
+throw_ptrace_error (const char *string)
+{
+  perror_with_name (string);
+}
diff --git a/gdb/nat/ptrace-utils.h b/gdb/nat/ptrace-utils.h
new file mode 100644
index 0000000..886d156
--- /dev/null
+++ b/gdb/nat/ptrace-utils.h
@@ -0,0 +1,28 @@
+/* ptrace utilities.
+
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+   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/>.  */
+
+#ifndef PTRACE_UTILS_H
+#define PTRACE_UTILS_H
+
+/* Translates an error out of a ptrace call to a GDB exception.  */
+
+extern void throw_ptrace_error (const char *string)
+  ATTRIBUTE_NORETURN;
+
+#endif
diff --git a/gdb/obsd-nat.c b/gdb/obsd-nat.c
index 72ed10c..2208d3b 100644
--- a/gdb/obsd-nat.c
+++ b/gdb/obsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "inf-child.h"
 #include "obsd-nat.h"
+#include "nat/ptrace-utils.h"
 
 /* OpenBSD 5.2 and later include rthreads which uses a thread model
    that maps userland threads directly onto kernel threads in a 1:1
@@ -58,7 +59,7 @@ obsd_update_thread_list (struct target_ops *ops)
   prune_threads ();
 
   if (ptrace (PT_GET_THREAD_FIRST, pid, (caddr_t)&pts, sizeof pts) == -1)
-    perror_with_name (("ptrace"));
+    throw_ptrace_error (("ptrace"));
 
   while (pts.pts_tid != -1)
     {
@@ -73,7 +74,7 @@ obsd_update_thread_list (struct target_ops *ops)
 	}
 
       if (ptrace (PT_GET_THREAD_NEXT, pid, (caddr_t)&pts, sizeof pts) == -1)
-	perror_with_name (("ptrace"));
+	throw_ptrace_error (("ptrace"));
     }
 }
 
@@ -123,7 +124,7 @@ obsd_wait (struct target_ops *ops,
       pid_t fpid;
 
       if (ptrace (PT_GET_PROCESS_STATE, pid, (caddr_t)&pe, sizeof pe) == -1)
-	perror_with_name (("ptrace"));
+	throw_ptrace_error (("ptrace"));
 
       switch (pe.pe_report_event)
 	{
@@ -138,7 +139,7 @@ obsd_wait (struct target_ops *ops,
 
 	  if (ptrace (PT_GET_PROCESS_STATE, fpid,
 		      (caddr_t)&pe, sizeof pe) == -1)
-	    perror_with_name (("ptrace"));
+	    throw_ptrace_error (("ptrace"));
 
 	  gdb_assert (pe.pe_report_event == PTRACE_FORK);
 	  gdb_assert (pe.pe_other_pid == pid);
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 88ca49e..779a84a 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -47,6 +47,7 @@
 #include "auxv.h"
 
 #include "nat/ppc-linux.h"
+#include "nat/ptrace-utils.h"
 
 /* Similarly for the hardware watchpoint support.  These requests are used
    when the PowerPC HWDEBUG ptrace interface is not available.  */
@@ -372,7 +373,7 @@ fetch_vsx_register (struct regcache *regcache, int tid, int regno)
 	  have_ptrace_getsetvsxregs = 0;
 	  return;
 	}
-      perror_with_name (_("Unable to fetch VSX register"));
+      throw_ptrace_error (_("Unable to fetch VSX register"));
     }
 
   regcache_raw_supply (regcache, regno,
@@ -401,7 +402,7 @@ fetch_altivec_register (struct regcache *regcache, int tid, int regno)
           have_ptrace_getvrregs = 0;
           return;
         }
-      perror_with_name (_("Unable to fetch AltiVec register"));
+      throw_ptrace_error (_("Unable to fetch AltiVec register"));
     }
  
   /* VSCR is fetched as a 16 bytes quantity, but it is really 4 bytes
@@ -439,7 +440,7 @@ get_spe_registers (int tid, struct gdb_evrregset_t *evrregset)
             have_ptrace_getsetevrregs = 0;
           else
             /* Anything else needs to be reported.  */
-            perror_with_name (_("Unable to fetch SPE registers"));
+            throw_ptrace_error (_("Unable to fetch SPE registers"));
         }
     }
 
@@ -553,7 +554,7 @@ fetch_register (struct regcache *regcache, int tid, int regno)
           char message[128];
 	  xsnprintf (message, sizeof (message), "reading register %s (#%d)",
 		     gdbarch_register_name (gdbarch, regno), regno);
-	  perror_with_name (message);
+	  throw_ptrace_error (message);
 	}
       memcpy (&buf[bytes_transferred], &l, sizeof (l));
     }
@@ -634,7 +635,7 @@ fetch_vsx_registers (struct regcache *regcache, int tid)
 	  have_ptrace_getsetvsxregs = 0;
 	  return;
 	}
-      perror_with_name (_("Unable to fetch VSX registers"));
+      throw_ptrace_error (_("Unable to fetch VSX registers"));
     }
   supply_vsxregset (regcache, &regs);
 }
@@ -653,7 +654,7 @@ fetch_altivec_registers (struct regcache *regcache, int tid)
           have_ptrace_getvrregs = 0;
 	  return;
 	}
-      perror_with_name (_("Unable to fetch AltiVec registers"));
+      throw_ptrace_error (_("Unable to fetch AltiVec registers"));
     }
   supply_vrregset (regcache, &regs);
 }
@@ -664,7 +665,7 @@ fetch_altivec_registers (struct regcache *regcache, int tid)
    
    If the ptrace request does not exist, this function returns 0
    and properly sets the have_ptrace_* flag.  If the request fails,
-   this function calls perror_with_name.  Otherwise, if the request
+   this function calls throw_ptrace_error.  Otherwise, if the request
    succeeds, then the regcache gets filled and 1 is returned.  */
 static int
 fetch_all_gp_regs (struct regcache *regcache, int tid)
@@ -680,7 +681,7 @@ fetch_all_gp_regs (struct regcache *regcache, int tid)
           have_ptrace_getsetregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't get general-purpose registers."));
+      throw_ptrace_error (_("Couldn't get general-purpose registers."));
     }
 
   supply_gregset (regcache, (const gdb_gregset_t *) &gregset);
@@ -718,7 +719,7 @@ fetch_gp_regs (struct regcache *regcache, int tid)
    
    If the ptrace request does not exist, this function returns 0
    and properly sets the have_ptrace_* flag.  If the request fails,
-   this function calls perror_with_name.  Otherwise, if the request
+   this function calls throw_ptrace_error.  Otherwise, if the request
    succeeds, then the regcache gets filled and 1 is returned.  */
 static int
 fetch_all_fp_regs (struct regcache *regcache, int tid)
@@ -732,7 +733,7 @@ fetch_all_fp_regs (struct regcache *regcache, int tid)
           have_ptrace_getsetfpregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't get floating-point registers."));
+      throw_ptrace_error (_("Couldn't get floating-point registers."));
     }
 
   supply_fpregset (regcache, (const gdb_fpregset_t *) &fpregs);
@@ -842,7 +843,7 @@ store_vsx_register (const struct regcache *regcache, int tid, int regno)
 	  have_ptrace_getsetvsxregs = 0;
 	  return;
 	}
-      perror_with_name (_("Unable to fetch VSX register"));
+      throw_ptrace_error (_("Unable to fetch VSX register"));
     }
 
   regcache_raw_collect (regcache, regno, regs +
@@ -850,7 +851,7 @@ store_vsx_register (const struct regcache *regcache, int tid, int regno)
 
   ret = ptrace (PTRACE_SETVSXREGS, tid, 0, &regs);
   if (ret < 0)
-    perror_with_name (_("Unable to store VSX register"));
+    throw_ptrace_error (_("Unable to store VSX register"));
 }
 
 /* Store one register.  */
@@ -872,7 +873,7 @@ store_altivec_register (const struct regcache *regcache, int tid, int regno)
           have_ptrace_getvrregs = 0;
           return;
         }
-      perror_with_name (_("Unable to fetch AltiVec register"));
+      throw_ptrace_error (_("Unable to fetch AltiVec register"));
     }
 
   /* VSCR is fetched as a 16 bytes quantity, but it is really 4 bytes
@@ -886,7 +887,7 @@ store_altivec_register (const struct regcache *regcache, int tid, int regno)
 
   ret = ptrace (PTRACE_SETVRREGS, tid, 0, &regs);
   if (ret < 0)
-    perror_with_name (_("Unable to store AltiVec register"));
+    throw_ptrace_error (_("Unable to store AltiVec register"));
 }
 
 /* Assuming TID referrs to an SPE process, set the top halves of TID's
@@ -913,7 +914,7 @@ set_spe_registers (int tid, struct gdb_evrregset_t *evrregset)
             have_ptrace_getsetevrregs = 0;
           else
             /* Anything else needs to be reported.  */
-            perror_with_name (_("Unable to set SPE registers"));
+            throw_ptrace_error (_("Unable to set SPE registers"));
         }
     }
 }
@@ -1047,7 +1048,7 @@ store_register (const struct regcache *regcache, int tid, int regno)
           char message[128];
 	  xsnprintf (message, sizeof (message), "writing register %s (#%d)",
 		     gdbarch_register_name (gdbarch, regno), regno);
-	  perror_with_name (message);
+	  throw_ptrace_error (message);
 	}
     }
 }
@@ -1102,13 +1103,13 @@ store_vsx_registers (const struct regcache *regcache, int tid)
 	  have_ptrace_getsetvsxregs = 0;
 	  return;
 	}
-      perror_with_name (_("Couldn't get VSX registers"));
+      throw_ptrace_error (_("Couldn't get VSX registers"));
     }
 
   fill_vsxregset (regcache, &regs);
 
   if (ptrace (PTRACE_SETVSXREGS, tid, 0, &regs) < 0)
-    perror_with_name (_("Couldn't write VSX registers"));
+    throw_ptrace_error (_("Couldn't write VSX registers"));
 }
 
 static void
@@ -1125,13 +1126,13 @@ store_altivec_registers (const struct regcache *regcache, int tid)
           have_ptrace_getvrregs = 0;
           return;
         }
-      perror_with_name (_("Couldn't get AltiVec registers"));
+      throw_ptrace_error (_("Couldn't get AltiVec registers"));
     }
 
   fill_vrregset (regcache, &regs);
   
   if (ptrace (PTRACE_SETVRREGS, tid, 0, &regs) < 0)
-    perror_with_name (_("Couldn't write AltiVec registers"));
+    throw_ptrace_error (_("Couldn't write AltiVec registers"));
 }
 
 /* This function actually issues the request to ptrace, telling
@@ -1140,7 +1141,7 @@ store_altivec_registers (const struct regcache *regcache, int tid)
    
    If the ptrace request does not exist, this function returns 0
    and properly sets the have_ptrace_* flag.  If the request fails,
-   this function calls perror_with_name.  Otherwise, if the request
+   this function calls throw_ptrace_error.  Otherwise, if the request
    succeeds, then the regcache is stored and 1 is returned.  */
 static int
 store_all_gp_regs (const struct regcache *regcache, int tid, int regno)
@@ -1156,7 +1157,7 @@ store_all_gp_regs (const struct regcache *regcache, int tid, int regno)
           have_ptrace_getsetregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't get general-purpose registers."));
+      throw_ptrace_error (_("Couldn't get general-purpose registers."));
     }
 
   fill_gregset (regcache, &gregset, regno);
@@ -1168,7 +1169,7 @@ store_all_gp_regs (const struct regcache *regcache, int tid, int regno)
           have_ptrace_getsetregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't set general-purpose registers."));
+      throw_ptrace_error (_("Couldn't set general-purpose registers."));
     }
 
   return 1;
@@ -1204,7 +1205,7 @@ store_gp_regs (const struct regcache *regcache, int tid, int regno)
    
    If the ptrace request does not exist, this function returns 0
    and properly sets the have_ptrace_* flag.  If the request fails,
-   this function calls perror_with_name.  Otherwise, if the request
+   this function calls throw_ptrace_error.  Otherwise, if the request
    succeeds, then the regcache is stored and 1 is returned.  */
 static int
 store_all_fp_regs (const struct regcache *regcache, int tid, int regno)
@@ -1218,7 +1219,7 @@ store_all_fp_regs (const struct regcache *regcache, int tid, int regno)
           have_ptrace_getsetfpregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't get floating-point registers."));
+      throw_ptrace_error (_("Couldn't get floating-point registers."));
     }
 
   fill_fpregset (regcache, &fpregs, regno);
@@ -1230,7 +1231,7 @@ store_all_fp_regs (const struct regcache *regcache, int tid, int regno)
           have_ptrace_getsetfpregs = 0;
           return 0;
         }
-      perror_with_name (_("Couldn't set floating-point registers."));
+      throw_ptrace_error (_("Couldn't set floating-point registers."));
     }
 
   return 1;
@@ -1559,7 +1560,7 @@ hwdebug_insert_point (struct ppc_hw_breakpoint *b, int tid)
   errno = 0;
   slot = ptrace (PPC_PTRACE_SETHWDEBUG, tid, 0, p);
   if (slot < 0)
-    perror_with_name (_("Unexpected error setting breakpoint or watchpoint"));
+    throw_ptrace_error (_("Unexpected error setting breakpoint or watchpoint"));
 
   /* Everything went fine, so we have to register this *point.  */
   t = hwdebug_find_thread_points_by_tid (tid, 1);
@@ -1606,7 +1607,7 @@ hwdebug_remove_point (struct ppc_hw_breakpoint *b, int tid)
   errno = 0;
   if (ptrace (PPC_PTRACE_DELHWDEBUG, tid, 0, hw_breaks[i].slot) < 0)
     if (errno != ENOENT)
-      perror_with_name (_("Unexpected error deleting "
+      throw_ptrace_error (_("Unexpected error deleting "
 			  "breakpoint or watchpoint"));
 
   xfree (hw_breaks[i].hw_break);
@@ -2419,7 +2420,7 @@ ppc_linux_read_description (struct target_ops *ops)
       /* EIO means that the PTRACE_GETEVRREGS request isn't supported.
 	 Anything else needs to be reported.  */
       else if (errno != EIO)
-	perror_with_name (_("Unable to fetch SPE registers"));
+	throw_ptrace_error (_("Unable to fetch SPE registers"));
     }
 
   if (have_ptrace_getsetvsxregs)
@@ -2432,7 +2433,7 @@ ppc_linux_read_description (struct target_ops *ops)
       /* EIO means that the PTRACE_GETVSXREGS request isn't supported.
 	 Anything else needs to be reported.  */
       else if (errno != EIO)
-	perror_with_name (_("Unable to fetch VSX registers"));
+	throw_ptrace_error (_("Unable to fetch VSX registers"));
     }
 
   if (have_ptrace_getvrregs)
@@ -2445,7 +2446,7 @@ ppc_linux_read_description (struct target_ops *ops)
       /* EIO means that the PTRACE_GETVRREGS request isn't supported.
 	 Anything else needs to be reported.  */
       else if (errno != EIO)
-	perror_with_name (_("Unable to fetch AltiVec registers"));
+	throw_ptrace_error (_("Unable to fetch AltiVec registers"));
     }
 
   /* Power ISA 2.05 (implemented by Power 6 and newer processors) increases
diff --git a/gdb/ppcfbsd-nat.c b/gdb/ppcfbsd-nat.c
index 778b4bb..544fe7a 100644
--- a/gdb/ppcfbsd-nat.c
+++ b/gdb/ppcfbsd-nat.c
@@ -35,6 +35,7 @@
 #include "ppc-tdep.h"
 #include "ppcfbsd-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "bsd-kvm.h"
 
 /* Fill GDB's register array with the general-purpose register values
@@ -123,7 +124,7 @@ ppcfbsd_fetch_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   supply_gregset (regcache, &regs);
 
@@ -134,7 +135,7 @@ ppcfbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	throw_ptrace_error (_("Couldn't get FP registers"));
 
       ppc_supply_fpregset (fpregset, regcache, regno, &fpregs, sizeof fpregs);
     }
@@ -151,13 +152,13 @@ ppcfbsd_store_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regno);
 
   if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 
   if (regno == -1 || getfpregs_supplies (get_regcache_arch (regcache), regno))
     {
@@ -165,13 +166,13 @@ ppcfbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	throw_ptrace_error (_("Couldn't get FP registers"));
 
       fill_fpregset (regcache, &fpregs, regno);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't set FP registers"));
+	throw_ptrace_error (_("Couldn't set FP registers"));
     }
 }
 
diff --git a/gdb/ppcnbsd-nat.c b/gdb/ppcnbsd-nat.c
index 59195c0..bc7e852 100644
--- a/gdb/ppcnbsd-nat.c
+++ b/gdb/ppcnbsd-nat.c
@@ -35,6 +35,7 @@
 #include "ppcnbsd-tdep.h"
 #include "bsd-kvm.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 /* Returns true if PT_GETREGS fetches this register.  */
 
@@ -88,7 +89,7 @@ ppcnbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-        perror_with_name (_("Couldn't get registers"));
+        throw_ptrace_error (_("Couldn't get registers"));
 
       ppc_supply_gregset (&ppcnbsd_gregset, regcache,
 			  regnum, &regs, sizeof regs);
@@ -100,7 +101,7 @@ ppcnbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	throw_ptrace_error (_("Couldn't get FP registers"));
 
       ppc_supply_fpregset (&ppcnbsd_fpregset, regcache,
 			   regnum, &fpregs, sizeof fpregs);
@@ -119,14 +120,14 @@ ppcnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       ppc_collect_gregset (&ppcnbsd_gregset, regcache,
 			   regnum, &regs, sizeof regs);
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't write registers"));
+	throw_ptrace_error (_("Couldn't write registers"));
     }
 
   if (regnum == -1 || getfpregs_supplies (gdbarch, regnum))
@@ -135,14 +136,14 @@ ppcnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get FP registers"));
+	throw_ptrace_error (_("Couldn't get FP registers"));
 
       ppc_collect_fpregset (&ppcnbsd_fpregset, regcache,
 			    regnum, &fpregs, sizeof fpregs);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't set FP registers"));
+	throw_ptrace_error (_("Couldn't set FP registers"));
     }
 }
 
diff --git a/gdb/ppcobsd-nat.c b/gdb/ppcobsd-nat.c
index eef7305..b1f3654 100644
--- a/gdb/ppcobsd-nat.c
+++ b/gdb/ppcobsd-nat.c
@@ -32,6 +32,7 @@
 #include "ppc-tdep.h"
 #include "ppcobsd-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "obsd-nat.h"
 #include "bsd-kvm.h"
 
@@ -78,7 +79,7 @@ ppcobsd_fetch_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   ppc_supply_gregset (&ppcobsd_gregset, regcache, -1,
 		      &regs, sizeof regs);
@@ -95,7 +96,7 @@ ppcobsd_fetch_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       ppc_supply_fpregset (&ppcobsd_fpregset, regcache, -1,
 			   &fpregs, sizeof fpregs);
@@ -114,7 +115,7 @@ ppcobsd_store_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   ppc_collect_gregset (&ppcobsd_gregset, regcache,
 		       regnum, &regs, sizeof regs);
@@ -125,7 +126,7 @@ ppcobsd_store_registers (struct target_ops *ops,
 
   if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 
 #ifdef PT_GETFPREGS
   if (regnum == -1
@@ -135,14 +136,14 @@ ppcobsd_store_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       ppc_collect_fpregset (&ppcobsd_fpregset, regcache,
 			    regnum, &fpregs, sizeof fpregs);
 
       if (ptrace (PT_SETFPREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't write floating point status"));
+	throw_ptrace_error (_("Couldn't write floating point status"));
     }
 #endif
 }
diff --git a/gdb/rs6000-nat.c b/gdb/rs6000-nat.c
index 8cdd86f..3c48a73 100644
--- a/gdb/rs6000-nat.c
+++ b/gdb/rs6000-nat.c
@@ -30,6 +30,7 @@
 #include "arch-utils.h"
 #include "inf-child.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "ppc-tdep.h"
 #include "rs6000-tdep.h"
 #include "rs6000-aix-tdep.h"
@@ -608,7 +609,7 @@ rs6000_ptrace_ldinfo (ptid_t ptid)
 	break; /* Success, we got the entire ld_info data.  */
 
       if (errno != ENOMEM)
-	perror_with_name (_("ptrace ldinfo"));
+	throw_ptrace_error (_("ptrace ldinfo"));
 
       /* ldi is not big enough.  Double it and try again.  */
       ldi_size *= 2;
diff --git a/gdb/s390-linux-nat.c b/gdb/s390-linux-nat.c
index 367b610..4837738 100644
--- a/gdb/s390-linux-nat.c
+++ b/gdb/s390-linux-nat.c
@@ -213,7 +213,7 @@ fetch_regs (struct regcache *regcache, int tid)
   parea.process_addr = (addr_t) &regs;
   parea.kernel_addr = offsetof (struct user_regs_struct, psw);
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   supply_gregset (regcache, (const gregset_t *) &regs);
 }
@@ -230,12 +230,12 @@ store_regs (const struct regcache *regcache, int tid, int regnum)
   parea.process_addr = (addr_t) &regs;
   parea.kernel_addr = offsetof (struct user_regs_struct, psw);
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regnum);
 
   if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 
 /* Fetch all floating-point registers from process/thread TID and store
@@ -250,7 +250,7 @@ fetch_fpregs (struct regcache *regcache, int tid)
   parea.process_addr = (addr_t) &fpregs;
   parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   supply_fpregset (regcache, (const fpregset_t *) &fpregs);
 }
@@ -267,12 +267,12 @@ store_fpregs (const struct regcache *regcache, int tid, int regnum)
   parea.process_addr = (addr_t) &fpregs;
   parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't get floating point status"));
+    throw_ptrace_error (_("Couldn't get floating point status"));
 
   fill_fpregset (regcache, &fpregs, regnum);
 
   if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
-    perror_with_name (_("Couldn't write floating point status"));
+    throw_ptrace_error (_("Couldn't write floating point status"));
 }
 
 /* Fetch all registers in the kernel's register set whose number is
@@ -294,7 +294,7 @@ fetch_regset (struct regcache *regcache, int tid,
       if (errno == ENODATA)
 	regcache_supply_regset (regset, regcache, -1, NULL, regsize);
       else
-	perror_with_name (_("Couldn't get register set"));
+	throw_ptrace_error (_("Couldn't get register set"));
     }
   else
     regcache_supply_regset (regset, regcache, -1, buf, regsize);
@@ -314,12 +314,12 @@ store_regset (struct regcache *regcache, int tid,
   iov.iov_len = regsize;
 
   if (ptrace (PTRACE_GETREGSET, tid, (long) regset_id, (long) &iov) < 0)
-    perror_with_name (_("Couldn't get register set"));
+    throw_ptrace_error (_("Couldn't get register set"));
 
   regcache_collect_regset (regset, regcache, -1, buf, regsize);
 
   if (ptrace (PTRACE_SETREGSET, tid, (long) regset_id, (long) &iov) < 0)
-    perror_with_name (_("Couldn't set register set"));
+    throw_ptrace_error (_("Couldn't set register set"));
 }
 
 /* Check whether the kernel provides a register set with number REGSET
@@ -449,7 +449,7 @@ s390_stopped_by_watchpoint (struct target_ops *ops)
   parea.process_addr = (addr_t) & per_lowcore;
   parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore);
   if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0)
-    perror_with_name (_("Couldn't retrieve watchpoint status"));
+    throw_ptrace_error (_("Couldn't retrieve watchpoint status"));
 
   result = (per_lowcore.perc_storage_alteration == 1
 	    && per_lowcore.perc_store_real_address == 0);
@@ -459,7 +459,7 @@ s390_stopped_by_watchpoint (struct target_ops *ops)
       /* Do not report this watchpoint again.  */
       memset (&per_lowcore, 0, sizeof (per_lowcore));
       if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0)
-	perror_with_name (_("Couldn't clear watchpoint status"));
+	throw_ptrace_error (_("Couldn't clear watchpoint status"));
     }
 
   return result;
@@ -490,7 +490,7 @@ s390_fix_watch_points (struct lwp_info *lp)
   parea.process_addr = (addr_t) & per_info;
   parea.kernel_addr = offsetof (struct user_regs_struct, per_info);
   if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0)
-    perror_with_name (_("Couldn't retrieve watchpoint status"));
+    throw_ptrace_error (_("Couldn't retrieve watchpoint status"));
 
   if (watch_base)
     {
@@ -506,7 +506,7 @@ s390_fix_watch_points (struct lwp_info *lp)
   per_info.ending_addr = watch_hi_addr;
 
   if (ptrace (PTRACE_POKEUSR_AREA, tid, &parea) < 0)
-    perror_with_name (_("Couldn't modify watchpoint status"));
+    throw_ptrace_error (_("Couldn't modify watchpoint status"));
 }
 
 static int
diff --git a/gdb/shnbsd-nat.c b/gdb/shnbsd-nat.c
index e0ed5e6..56e5943 100644
--- a/gdb/shnbsd-nat.c
+++ b/gdb/shnbsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "sh-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "regcache.h"
 
 
@@ -51,7 +52,7 @@ shnbsd_fetch_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &inferior_registers, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       sh_corefile_supply_regset (&sh_corefile_gregset, regcache, regno,
 				 (char *) &inferior_registers,
@@ -72,7 +73,7 @@ shnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &inferior_registers, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       sh_corefile_collect_regset (&sh_corefile_gregset, regcache, regno,
 				  (char *) &inferior_registers,
@@ -80,7 +81,7 @@ shnbsd_store_inferior_registers (struct target_ops *ops,
 
       if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 		  (PTRACE_TYPE_ARG3) &inferior_registers, 0) == -1)
-	perror_with_name (_("Couldn't set registers"));
+	throw_ptrace_error (_("Couldn't set registers"));
 
       if (regno != -1)
 	return;
diff --git a/gdb/sparc-nat.c b/gdb/sparc-nat.c
index f1d82e5..152f6bb 100644
--- a/gdb/sparc-nat.c
+++ b/gdb/sparc-nat.c
@@ -32,6 +32,7 @@
 #include "sparc-tdep.h"
 #include "sparc-nat.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 /* With some trickery we can use the code in this file for most (if
    not all) ptrace(2) based SPARC systems, which includes SunOS 4,
@@ -170,7 +171,7 @@ sparc_fetch_inferior_registers (struct target_ops *ops,
       gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       sparc_supply_gregset (sparc_gregmap, regcache, -1, &regs);
       if (regnum != -1)
@@ -182,7 +183,7 @@ sparc_fetch_inferior_registers (struct target_ops *ops,
       fpregset_t fpregs;
 
       if (ptrace (PTRACE_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating point status"));
+	throw_ptrace_error (_("Couldn't get floating point status"));
 
       sparc_supply_fpregset (sparc_fpregmap, regcache, -1, &fpregs);
     }
@@ -206,12 +207,12 @@ sparc_store_inferior_registers (struct target_ops *ops,
       gregset_t regs;
 
       if (ptrace (PTRACE_GETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't get registers"));
+	throw_ptrace_error (_("Couldn't get registers"));
 
       sparc_collect_gregset (sparc_gregmap, regcache, regnum, &regs);
 
       if (ptrace (PTRACE_SETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-	perror_with_name (_("Couldn't write registers"));
+	throw_ptrace_error (_("Couldn't write registers"));
 
       /* Deal with the stack regs.  */
       if (regnum == -1 || regnum == SPARC_SP_REGNUM
@@ -232,7 +233,7 @@ sparc_store_inferior_registers (struct target_ops *ops,
       fpregset_t fpregs, saved_fpregs;
 
       if (ptrace (PTRACE_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	perror_with_name (_("Couldn't get floating-point registers"));
+	throw_ptrace_error (_("Couldn't get floating-point registers"));
 
       memcpy (&saved_fpregs, &fpregs, sizeof (fpregs));
       sparc_collect_fpregset (sparc_fpregmap, regcache, regnum, &fpregs);
@@ -245,7 +246,7 @@ sparc_store_inferior_registers (struct target_ops *ops,
 	{
 	  if (ptrace (PTRACE_SETFPREGS, pid,
 		      (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
-	    perror_with_name (_("Couldn't write floating-point registers"));
+	    throw_ptrace_error (_("Couldn't write floating-point registers"));
 	}
 
       if (regnum != -1)
@@ -291,7 +292,7 @@ sparc_xfer_wcookie (struct target_ops *ops, enum target_object object,
     if (ptrace (PT_WCOOKIE, pid, (PTRACE_TYPE_ARG3) &wcookie, 0) == -1)
       {
 	if (errno != EINVAL)
-	  perror_with_name (_("Couldn't get StackGhost cookie"));
+	  throw_ptrace_error (_("Couldn't get StackGhost cookie"));
 
 	/* Although PT_WCOOKIE is defined on OpenBSD 3.1 and later,
 	   the request wasn't implemented until after OpenBSD 3.4.  If
diff --git a/gdb/spu-linux-nat.c b/gdb/spu-linux-nat.c
index b0942a9..cf0f4df 100644
--- a/gdb/spu-linux-nat.c
+++ b/gdb/spu-linux-nat.c
@@ -24,6 +24,7 @@
 #include "inferior.h"
 #include "inf-child.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 #include "regcache.h"
 #include "symfile.h"
 #include "gdb_wait.h"
@@ -77,7 +78,7 @@ fetch_ppc_register (int regno)
     {
       char mess[128];
       xsnprintf (mess, sizeof mess, "reading PPC register #%d", regno);
-      perror_with_name (_(mess));
+      throw_ptrace_error (_(mess));
     }
 
   return (ULONGEST) (unsigned long) res;
diff --git a/gdb/tilegx-linux-nat.c b/gdb/tilegx-linux-nat.c
index b8f0c76..68c2391 100644
--- a/gdb/tilegx-linux-nat.c
+++ b/gdb/tilegx-linux-nat.c
@@ -133,7 +133,7 @@ fetch_inferior_registers (struct target_ops *ops,
     tid = ptid_get_pid (inferior_ptid);
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (PTRACE_TYPE_ARG3) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   supply_gregset (regcache, (const elf_gregset_t *)&regs);
 }
@@ -153,12 +153,12 @@ store_inferior_registers (struct target_ops *ops,
     tid = ptid_get_pid (inferior_ptid);
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (PTRACE_TYPE_ARG3) &regs) < 0)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   fill_gregset (regcache, &regs, regnum);
 
   if (ptrace (PTRACE_SETREGS, tid, 0, (PTRACE_TYPE_ARG3) &regs) < 0)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 
 
diff --git a/gdb/vaxbsd-nat.c b/gdb/vaxbsd-nat.c
index 866b958..7caae30 100644
--- a/gdb/vaxbsd-nat.c
+++ b/gdb/vaxbsd-nat.c
@@ -28,6 +28,7 @@
 
 #include "vax-tdep.h"
 #include "inf-ptrace.h"
+#include "nat/ptrace-utils.h"
 
 /* Supply the general-purpose registers stored in GREGS to REGCACHE.  */
 
@@ -70,7 +71,7 @@ vaxbsd_fetch_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   vaxbsd_supply_gregset (regcache, &regs);
 }
@@ -86,13 +87,13 @@ vaxbsd_store_inferior_registers (struct target_ops *ops,
 
   if (ptrace (PT_GETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't get registers"));
+    throw_ptrace_error (_("Couldn't get registers"));
 
   vaxbsd_collect_gregset (regcache, &regs, regnum);
 
   if (ptrace (PT_SETREGS, ptid_get_pid (inferior_ptid),
 	      (PTRACE_TYPE_ARG3) &regs, 0) == -1)
-    perror_with_name (_("Couldn't write registers"));
+    throw_ptrace_error (_("Couldn't write registers"));
 }
 \f
 
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index 26ae0b8..d184e41 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -28,6 +28,7 @@
 
 #include "x86-nat.h"
 #include "linux-nat.h"
+#include "nat/ptrace-utils.h"
 #ifndef __x86_64__
 #include "i386-linux-nat.h"
 #endif
@@ -78,7 +79,7 @@ x86_linux_dr_get (ptid_t ptid, int regnum)
   value = ptrace (PTRACE_PEEKUSER, tid, u_debugreg_offset (regnum), 0);
 
   if (errno != 0)
-    perror_with_name (_("Couldn't read debug register"));
+    throw_ptrace_error (_("Couldn't read debug register"));
 
   return value;
 }
@@ -96,7 +97,7 @@ x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value)
   errno = 0;
   ptrace (PTRACE_POKEUSER, tid, u_debugreg_offset (regnum), value);
   if (errno != 0)
-    perror_with_name (_("Couldn't write debug register"));
+    throw_ptrace_error (_("Couldn't write debug register"));
 }
 
 /* Return the inferior's debug register REGNUM.  */
@@ -323,7 +324,7 @@ x86_linux_read_description (struct target_ops *ops)
     cs = ptrace (PTRACE_PEEKUSER, tid,
 		 offsetof (struct user_regs_struct, cs), 0);
     if (errno != 0)
-      perror_with_name (_("Couldn't get CS register"));
+      throw_ptrace_error (_("Couldn't get CS register"));
 
     is_64bit = cs == AMD64_LINUX_USER64_CS;
 
@@ -332,7 +333,7 @@ x86_linux_read_description (struct target_ops *ops)
     ds = ptrace (PTRACE_PEEKUSER, tid,
 		 offsetof (struct user_regs_struct, ds), 0);
     if (errno != 0)
-      perror_with_name (_("Couldn't get DS register"));
+      throw_ptrace_error (_("Couldn't get DS register"));
 
     is_x32 = ds == AMD64_LINUX_X32_DS;
 
diff --git a/gdb/xtensa-linux-nat.c b/gdb/xtensa-linux-nat.c
index 921bc99..428fe37 100644
--- a/gdb/xtensa-linux-nat.c
+++ b/gdb/xtensa-linux-nat.c
@@ -189,7 +189,7 @@ fetch_gregs (struct regcache *regcache, int regnum)
   
   if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
     {
-      perror_with_name (_("Couldn't get registers"));
+      throw_ptrace_error (_("Couldn't get registers"));
       return;
     }
  
@@ -208,7 +208,7 @@ store_gregs (struct regcache *regcache, int regnum)
 
   if (ptrace (PTRACE_GETREGS, tid, 0, (long) &regs) < 0)
     {
-      perror_with_name (_("Couldn't get registers"));
+      throw_ptrace_error (_("Couldn't get registers"));
       return;
     }
 
@@ -216,7 +216,7 @@ store_gregs (struct regcache *regcache, int regnum)
 
   if (ptrace (PTRACE_SETREGS, tid, 0, (long) &regs) < 0)
     {
-      perror_with_name (_("Couldn't write registers"));
+      throw_ptrace_error (_("Couldn't write registers"));
       return;
     }
 }
@@ -235,7 +235,7 @@ fetch_xtregs (struct regcache *regcache, int regnum)
   char xtregs [XTENSA_ELF_XTREG_SIZE];
 
   if (ptrace (PTRACE_GETXTREGS, tid, 0, (long)&xtregs) < 0)
-    perror_with_name (_("Couldn't get extended registers"));
+    throw_ptrace_error (_("Couldn't get extended registers"));
 
   for (ptr = xtensa_regmap_table; ptr->name; ptr++)
     if (regnum == ptr->gdb_regnum || regnum == -1)
@@ -251,7 +251,7 @@ store_xtregs (struct regcache *regcache, int regnum)
   char xtregs [XTENSA_ELF_XTREG_SIZE];
 
   if (ptrace (PTRACE_GETXTREGS, tid, 0, (long)&xtregs) < 0)
-    perror_with_name (_("Couldn't get extended registers"));
+    throw_ptrace_error (_("Couldn't get extended registers"));
 
   for (ptr = xtensa_regmap_table; ptr->name; ptr++)
     if (regnum == ptr->gdb_regnum || regnum == -1)
@@ -259,7 +259,7 @@ store_xtregs (struct regcache *regcache, int regnum)
 			    xtregs + ptr->ptrace_offset);
 
   if (ptrace (PTRACE_SETXTREGS, tid, 0, (long)&xtregs) < 0)
-    perror_with_name (_("Couldn't write extended registers"));
+    throw_ptrace_error (_("Couldn't write extended registers"));
 }
 
 void
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB
  2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
                   ` (4 preceding siblings ...)
  2015-03-06 19:58 ` [PATCH 5/6] gdbserver/Linux: internal error when killing a process that is already gone Pedro Alves
@ 2015-03-06 20:27 ` Pedro Alves
  2015-07-14 10:22   ` Pedro Alves
  5 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 20:27 UTC (permalink / raw)
  To: gdb-patches

This exercises the case of the inferior disappearing while GDB is
debugging it, such as something doing "kill -9 PID" while the program
is stopped under GDB or GDBserver.  This triggered a set of internal
errors, fixed by the previous patches.

gdb/testsuite/ChangeLog:
2015-03-06  Pedro Alves  <palves@redhat.com>

	* gdb.base/killed-outside.exp: New file.
	* gdb.base/killed-outside.c: New file.
---
 gdb/testsuite/gdb.base/killed-outside.c   |  34 ++++++++
 gdb/testsuite/gdb.base/killed-outside.exp | 130 ++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+)
 create mode 100644 gdb/testsuite/gdb.base/killed-outside.c
 create mode 100644 gdb/testsuite/gdb.base/killed-outside.exp

diff --git a/gdb/testsuite/gdb.base/killed-outside.c b/gdb/testsuite/gdb.base/killed-outside.c
new file mode 100644
index 0000000..f0e9eda
--- /dev/null
+++ b/gdb/testsuite/gdb.base/killed-outside.c
@@ -0,0 +1,34 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   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 <sys/types.h>
+#include <unistd.h>
+
+pid_t pid;
+
+static void
+done (void)
+{
+}
+
+int
+main (int argc, char **argv)
+{
+  pid = getpid ();
+  done ();
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.base/killed-outside.exp b/gdb/testsuite/gdb.base/killed-outside.exp
new file mode 100644
index 0000000..1e8c0d3
--- /dev/null
+++ b/gdb/testsuite/gdb.base/killed-outside.exp
@@ -0,0 +1,130 @@
+# Copyright 2015 Free Software Foundation, Inc.
+
+# 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/>.
+
+# Test that GDB doesn't get badly wedged if the inferior is killed
+# from outside GDB (with SIGKILL) while the program is stopped.
+
+standard_testfile
+
+# Get the value of variable VAR in the inferior.  MSG is used as the
+# test message.
+
+proc get_value {var msg} {
+    global expect_out
+    global gdb_prompt
+    global decimal
+
+    set value -1
+    gdb_test_multiple "print $var" "$msg" {
+	-re ".*= ($decimal).*\r\n$gdb_prompt $" {
+	    set value $expect_out(1,string)
+	    pass "$msg"
+        }
+    }
+    return ${value}
+}
+
+# Runs the program until a breakpoint, deletes all breakpoints, and
+# then kills the inferior from _outside_ GDB, with SIGKILL.  Runs CMDS
+# afterwards, to make sure GDB copes with the inferior disappearing,
+# and then quits GDB.
+
+proc test {cmds_after_kill} {
+    global binfile
+    global gdb_prompt
+    global decimal
+
+    clean_restart ${binfile}
+
+    if ![runto done] {
+	return
+    }
+
+    # So that "continue" doesn't try a step over, etc.
+    delete_breakpoints
+
+    set testpid [get_value "pid" "get pid of inferior"]
+    if { $testpid == -1 } {
+	return -1
+    }
+
+    remote_exec target "kill -9 ${testpid}"
+
+    # Give it some time to die.
+    sleep 2
+
+    uplevel 1 $cmds_after_kill
+
+    # Make sure we can quit.
+    set msg "quit GDB"
+    gdb_test_multiple "quit" $msg {
+	-re "Quit anyway\\? \\(y or n\\) $" {
+	    send_gdb "y\n"
+	    exp_continue
+	}
+	eof {
+	    pass $msg
+	}
+    }
+}
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile $options] == -1} {
+    return -1
+}
+
+# The actual output GDB prints in response to commands after the
+# inferior is gone isn't very well defined, and will depend on target.
+# What we're trying to make sure is that GDB doesn't internal error or
+# get wedged.
+
+# Try simply continuing.
+with_test_prefix "continue" {
+    test {
+	# Try stepping the program.  Stepping may need to read/write
+	# registers, unlike continue.
+	gdb_test "continue" ".*"
+
+	# Try listing threads afterwards.  It's probably what the user
+	# will do after an error.
+	gdb_test "info threads" ".*"
+    }
+}
+
+# Try stepping the program.  Stepping may go through diferent code
+# paths in the target backends.
+with_test_prefix "stepi" {
+    test {
+	gdb_test "si" ".*"
+	gdb_test "info threads" ".*"
+    }
+}
+
+# Try fetching registers explicitly, which should cover the error many
+# other commands would trigger.
+with_test_prefix "registers" {
+    test {
+	gdb_test "flushregs" ".*"
+	gdb_test "info threads" ".*"
+    }
+}
+
+# Try only listing threads explicitly, first thing, which is another
+# operation GDB may or not decide to do itself and is likely to be
+# what a user would try after error too.
+with_test_prefix "info threads" {
+    test {
+	gdb_test "info threads" ".*"
+    }
+}
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-06 19:58 ` [PATCH 2/6] Introduce throw_ptrace_error Pedro Alves
@ 2015-03-06 21:04   ` Mark Kettenis
  2015-03-06 21:40     ` Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2015-03-06 21:04 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

> From: Pedro Alves <palves@redhat.com>
> Date: Fri,  6 Mar 2015 19:58:02 +0000
> 
> This adds a new function that is meant to be called instead of
> perror_with_name whenever we get an error out of ptrace.  The
> idea is to convert some errno errors to different GDB exceptions in
> a following patch.

What is ptrace-specific about throwing an error?  This really feels
like the wrong direction to me.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-06 21:04   ` Mark Kettenis
@ 2015-03-06 21:40     ` Pedro Alves
  2015-03-08 20:30       ` Mark Kettenis
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-06 21:40 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

On 03/06/2015 09:03 PM, Mark Kettenis wrote:
>> From: Pedro Alves <palves@redhat.com>
>> Date: Fri,  6 Mar 2015 19:58:02 +0000
>>
>> This adds a new function that is meant to be called instead of
>> perror_with_name whenever we get an error out of ptrace.  The
>> idea is to convert some errno errors to different GDB exceptions in
>> a following patch.
> 
> What is ptrace-specific about throwing an error?  This really feels
> like the wrong direction to me.

Not exactly sure what you mean.  Throwing an error is
of course not ptrace-specific.  What is ptrace-specific is the
interpretation of errno.  The end result of the series is that
an ESRCH as a result of a ptrace error ends up throwing
a THREAD_NOT_FOUND_ERROR error instead of a GENERIC_ERROR.  Then
callers up the chain can actually distinguish the errors.

Please take a look at patch 3.  It should make things clearer.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-06 21:40     ` Pedro Alves
@ 2015-03-08 20:30       ` Mark Kettenis
  2015-03-08 21:48         ` Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2015-03-08 20:30 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

> Date: Fri, 06 Mar 2015 21:40:03 +0000
> From: Pedro Alves <palves@redhat.com>
> 
> On 03/06/2015 09:03 PM, Mark Kettenis wrote:
> >> From: Pedro Alves <palves@redhat.com>
> >> Date: Fri,  6 Mar 2015 19:58:02 +0000
> >>
> >> This adds a new function that is meant to be called instead of
> >> perror_with_name whenever we get an error out of ptrace.  The
> >> idea is to convert some errno errors to different GDB exceptions in
> >> a following patch.
> > 
> > What is ptrace-specific about throwing an error?  This really feels
> > like the wrong direction to me.
> 
> Not exactly sure what you mean.  Throwing an error is
> of course not ptrace-specific.  What is ptrace-specific is the
> interpretation of errno.  The end result of the series is that
> an ESRCH as a result of a ptrace error ends up throwing
> a THREAD_NOT_FOUND_ERROR error instead of a GENERIC_ERROR.  Then
> callers up the chain can actually distinguish the errors.
> 
> Please take a look at patch 3.  It should make things clearer.

I think your interpretation of ESRCH is too Linux-centric.  You're
once again duct-taping around the Linux kernel's whoefully
insufficient threads debugging capabilities.  It really should not be
possible for a thread to just disappear without the debugger being
notified.  Do I sound like a broken record?

I think at this point the right approach is to make
linux_resume_one_lwp() call ptrace() directly instead of calling down
into the inf_ptrace_resume().  That way you can simply check errno in
the place where it matters.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-08 20:30       ` Mark Kettenis
@ 2015-03-08 21:48         ` Pedro Alves
  2015-03-10 14:53           ` Mark Kettenis
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-08 21:48 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

On 03/08/2015 08:29 PM, Mark Kettenis wrote:

> I think your interpretation of ESRCH is too Linux-centric.  You're
> once again duct-taping around the Linux kernel's whoefully
> insufficient threads debugging capabilities.

Nice.

> It really should not be
> possible for a thread to just disappear without the debugger being
> notified.  Do I sound like a broken record?

Sorry, but yes, you do.  ;-)

The debugger is notified.  It's just a fact that a process can
die (and become zombie) even while it was _stopped_ under
ptrace control.  That's a race you can't prevent, only cope with.

I found NetBSD 5.1 in the GCC compile farm, and I see ESRCH
there too:

-bash-4.2$ uname -a
NetBSD gcc70.fsffrance.org 5.1 NetBSD 5.1 (GENERIC) #0: Sat Nov  6 13:19:33 UTC 2010  builds@b6.netbsd.org:/home/builds/ab/netbsd-5-1-RELEASE/amd64/201011061943Z-obj/home/builds/ab/netbsd-5-1-RELEASE/src/sys/arch/amd64/compile/GENERIC amd64

-bash-4.2$ gdb ./foo
GNU gdb 6.5
...
(gdb) start
Breakpoint 1 at 0x400894: file foo.c, line 5.
Starting program: /home/palves/foo
main () at foo.c:5
5         return 0;
(gdb) p getpid ()
$1 = 24557
(gdb) shell kill -9 24557
(gdb) c
Continuing.
ptrace: No such process.
(gdb)

But even if some ptrace-based OS uses a different errno
for that (which I doubt), we can just tweak throw_ptrace_error
(a centralized place, yay!) to look for a different
errno value.  So what does OpenBSD's ptrace return
in the test above?

> I think at this point the right approach is to make
> linux_resume_one_lwp() call ptrace() directly instead of calling down
> into the inf_ptrace_resume().  That way you can simply check errno in
> the place where it matters.

No, your "simply" is not simple as you imply.  There can be any number
of ptrace calls that fail before the PT_CONTINUE in inf_ptrace_resume
is reached.  And whether to ignore the error should be left to some
caller higher up on the call chain.  That was the _whole point_ of this
fuller fix, as I explained throughout the series.
E.g., the ptrace call that fails can be the one that tries to write
debug registers to the inferior, normal registers, reading the auxv,
any memory read/write, whatever.  Any ptrace error that throws ends up
in the generic perror_with_name today, after the series, they'll
end up in throw_ptrace_error instead, a single place we can add
more context info to the error thrown.  How is that a bad thing?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-08 21:48         ` Pedro Alves
@ 2015-03-10 14:53           ` Mark Kettenis
  2015-03-11 15:44             ` Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Mark Kettenis @ 2015-03-10 14:53 UTC (permalink / raw)
  To: palves; +Cc: gdb-patches

> Date: Sun, 08 Mar 2015 21:48:12 +0000
> From: Pedro Alves <palves@redhat.com>
> 
> On 03/08/2015 08:29 PM, Mark Kettenis wrote:
> 
> > I think your interpretation of ESRCH is too Linux-centric.  You're
> > once again duct-taping around the Linux kernel's whoefully
> > insufficient threads debugging capabilities.
> 
> Nice.
> 
> > It really should not be
> > possible for a thread to just disappear without the debugger being
> > notified.  Do I sound like a broken record?
> 
> Sorry, but yes, you do.  ;-)
> 
> The debugger is notified.  It's just a fact that a process can
> die (and become zombie) even while it was _stopped_ under
> ptrace control.  That's a race you can't prevent, only cope with.

A process yes, but a thread no.

> I found NetBSD 5.1 in the GCC compile farm, and I see ESRCH
> there too:
> 
> -bash-4.2$ uname -a
> NetBSD gcc70.fsffrance.org 5.1 NetBSD 5.1 (GENERIC) #0: Sat Nov  6 13:19:33 UTC 2010  builds@b6.netbsd.org:/home/builds/ab/netbsd-5-1-RELEASE/amd64/201011061943Z-obj/home/builds/ab/netbsd-5-1-RELEASE/src/sys/arch/amd64/compile/GENERIC amd64
> 
> -bash-4.2$ gdb ./foo
> GNU gdb 6.5
> ...
> (gdb) start
> Breakpoint 1 at 0x400894: file foo.c, line 5.
> Starting program: /home/palves/foo
> main () at foo.c:5
> 5         return 0;
> (gdb) p getpid ()
> $1 = 24557
> (gdb) shell kill -9 24557
> (gdb) c
> Continuing.
> ptrace: No such process.
> (gdb)

That's an ancient GDB though.

> But even if some ptrace-based OS uses a different errno
> for that (which I doubt), we can just tweak throw_ptrace_error
> (a centralized place, yay!) to look for a different
> errno value.  So what does OpenBSD's ptrace return
> in the test above?

(gdb) p getpid()
$1 = 24737
(gdb) shell kill -9 24747
ksh: kill: 24747: No such process
(gdb) shell kill -9 24737
(gdb) c
Continuing.

Program received signal SIGKILL, Killed.
main () at ../../../../src/binutils-gdb/gdb/testsuite/gdb.base/wchar.c:29
29        wchar_t narrow = 97;
(gdb) 

Which strikes me as the proper behaviour in this case.  Not sure if
the NetBSD behaviour you're seeing is the result of different GDB
code, or really different kernel behaviour.

OpenBSD's ptrace(2) will set errno to ESRCH if you pass it a
non-existant process ID or thread ID.  As can be seen when using the
"attach" command:

(gdb) attach 666
Attaching to program: /home/kettenis/obj/binutils-gdb/gdb/testsuite/gdb.base/wchar, process 666
ptrace: No such process

Perhaps the error message here could be improved, but that doesn't
require us to add a throw with more details here.

In the end the meaning of ESRCH is dependent on the context.  You
can't just interpret it as a missing thread.

> > I think at this point the right approach is to make
> > linux_resume_one_lwp() call ptrace() directly instead of calling down
> > into the inf_ptrace_resume().  That way you can simply check errno in
> > the place where it matters.
> 
> No, your "simply" is not simple as you imply.  There can be any number
> of ptrace calls that fail before the PT_CONTINUE in inf_ptrace_resume
> is reached.  And whether to ignore the error should be left to some
> caller higher up on the call chain.  That was the _whole point_ of this
> fuller fix, as I explained throughout the series.
> E.g., the ptrace call that fails can be the one that tries to write
> debug registers to the inferior, normal registers, reading the auxv,
> any memory read/write, whatever.  Any ptrace error that throws ends up
> in the generic perror_with_name today, after the series, they'll
> end up in throw_ptrace_error instead, a single place we can add
> more context info to the error thrown.  How is that a bad thing?

The problem is that you'll always end up losing some context by using
a throw/catch model.  I think that should be avoided whenever
possible, and I got the impression that in the specific race you were
trying to fix here it could be avoided.

It also strikes me as undesitable having to add a new an dfairly
generic file to the list in config/*.mh.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 2/6] Introduce throw_ptrace_error
  2015-03-10 14:53           ` Mark Kettenis
@ 2015-03-11 15:44             ` Pedro Alves
  2015-03-19 17:33               ` [pushed] Fix race exposed by gdb.threads/killed.exp (Re: [PATCH 2/6] Introduce throw_ptrace_error) Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-11 15:44 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

On 03/10/2015 02:53 PM, Mark Kettenis wrote:
>> Date: Sun, 08 Mar 2015 21:48:12 +0000
>> From: Pedro Alves <palves@redhat.com>
>>
>> On 03/08/2015 08:29 PM, Mark Kettenis wrote:
>>
>> The debugger is notified.  It's just a fact that a process can
>> die (and become zombie) even while it was _stopped_ under
>> ptrace control.  That's a race you can't prevent, only cope with.
> 
> A process yes, but a thread no.

Threads die with the process, so...  And we can probably
observe stopped threads dying without the "process" dying
with programs that use raw clone instead of pthreads.

> 
>> I found NetBSD 5.1 in the GCC compile farm, and I see ESRCH
>> there too:
>>
>> -bash-4.2$ uname -a
>> NetBSD gcc70.fsffrance.org 5.1 NetBSD 5.1 (GENERIC) #0: Sat Nov  6 13:19:33 UTC 2010  builds@b6.netbsd.org:/home/builds/ab/netbsd-5-1-RELEASE/amd64/201011061943Z-obj/home/builds/ab/netbsd-5-1-RELEASE/src/sys/arch/amd64/compile/GENERIC amd64
>>
>> -bash-4.2$ gdb ./foo
>> GNU gdb 6.5
>> ...
>> (gdb) start
>> Breakpoint 1 at 0x400894: file foo.c, line 5.
>> Starting program: /home/palves/foo
>> main () at foo.c:5
>> 5         return 0;
>> (gdb) p getpid ()
>> $1 = 24557
>> (gdb) shell kill -9 24557
>> (gdb) c
>> Continuing.
>> ptrace: No such process.
>> (gdb)
> 
> That's an ancient GDB though.
> 
>> But even if some ptrace-based OS uses a different errno
>> for that (which I doubt), we can just tweak throw_ptrace_error
>> (a centralized place, yay!) to look for a different
>> errno value.  So what does OpenBSD's ptrace return
>> in the test above?
> 
> (gdb) p getpid()
> $1 = 24737
> (gdb) shell kill -9 24747
> ksh: kill: 24747: No such process
> (gdb) shell kill -9 24737
> (gdb) c
> Continuing.
> 
> Program received signal SIGKILL, Killed.
> main () at ../../../../src/binutils-gdb/gdb/testsuite/gdb.base/wchar.c:29
> 29        wchar_t narrow = 97;
> (gdb) 

That "Program received" instead of "Program exited with ..." just
shows that a ptracer can intercept SIGKILL on OpenBSD...  Does that
mean that a ptracer can suppress SIGKILL then?

At least on Linux and (AFAICS) NetBSD, SIGKILL cannot be caught.

I'm sure there must be situations where the kernel or wheel/root can
make a user process disappear under the user's debugger's feet
in OpenBSD as well.

Here's what I see on Linux, after the fix:

(gdb) shell kill -9 492
(gdb) c
Continuing.

Program terminated with signal SIGKILL, Killed.
The program no longer exists.
(gdb)

Though note that more fixing is necessary through out to
fix all this better.  E.g., that "continue" works because
GDB didn't try to insert any breakpoint in the main program.
If it does, we get:

(gdb) c
Continuing.
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x4004c8

and again:

(gdb) c
Continuing.
Warning:
Cannot insert breakpoint 2.
Cannot access memory at address 0x4004c8

forever and ever.  IOW, it's not possible to continue
to collect the wait status.

Or we get seemingly strange (for a user) things like:

(gdb) info threads
  Id   Target Id         Frame
* 1    process 615 "main" main (argc=<error reading variable: Cannot access memory at address 0x7fffffffd83c>,
    argv=<error reading variable: Cannot access memory at address 0x7fffffffd830>) at main.c:5
(gdb)

why am I getting these errors, argh!"

Yeah, GDB could have told the user why, but it didn't.

I think that to fix this we'll need several TRY/CATCH around some
strategic places in the core.  I suspect we'll also need to
model a "zombie" thread state (different from "exited" in that
there's a wait status to collect).

> Which strikes me as the proper behaviour in this case.  Not sure if
> the NetBSD behaviour you're seeing is the result of different GDB
> code, or really different kernel behaviour.

I'm sure it must be different kernel behavior.  I saw the same with
mainline gdb when I tried it a couple days ago.  The machine seems to
be down now, though... (It's gcc70.fsffrance.org.  I filed a support
request with the gcc compiler farm folks).

> 
> OpenBSD's ptrace(2) will set errno to ESRCH if you pass it a
> non-existant process ID or thread ID.  As can be seen when using the
> "attach" command:
> 
> (gdb) attach 666
> Attaching to program: /home/kettenis/obj/binutils-gdb/gdb/testsuite/gdb.base/wchar, process 666
> ptrace: No such process
> 
> Perhaps the error message here could be improved, but that doesn't
> require us to add a throw with more details here.

Sure, same thing with Linux's ptrace.

But, that's clearly a case where you _want_ the error to
end up at the command level.

> In the end the meaning of ESRCH is dependent on the context.  You
> can't just interpret it as a missing thread.

The error is THREAD_NOT_FOUND_ERROR.  Saying "search for process
failed" (ESRCH) or "process not found" sounds like exactly the same
to my ears.  There's no interpretation intended right there.  Sure,
OK, I could have called it PROCESS_NOT_FOUND_ERROR.  I called it
THREAD because it looks to me that if the process is not found, so
can't its threads, so saying the (kernel) thread/task that was passed
to ptrace wasn't found is correct too, while the opposite is not true: if
a thread is not found, but it's containing process (thread group
for Linux) exists alive, then PROCESS_NOT_FOUND_ERROR wouldn't be
correct.

The "thread is gone" part was indeed interpretation, but that was
in the code that _catches_ the exception.  In the case in question, that's
exactly the right context -- we know we were attached to the process,
otherwise we wouldn't be trying to resume it!  So if ptrace can't find
it, "thread/process not found" means "thread/process is gone".

>>> I think at this point the right approach is to make
>>> linux_resume_one_lwp() call ptrace() directly instead of calling down
>>> into the inf_ptrace_resume().  That way you can simply check errno in
>>> the place where it matters.
>>
>> No, your "simply" is not simple as you imply.  There can be any number
>> of ptrace calls that fail before the PT_CONTINUE in inf_ptrace_resume
>> is reached.  And whether to ignore the error should be left to some
>> caller higher up on the call chain.  That was the _whole point_ of this
>> fuller fix, as I explained throughout the series.
>> E.g., the ptrace call that fails can be the one that tries to write
>> debug registers to the inferior, normal registers, reading the auxv,
>> any memory read/write, whatever.  Any ptrace error that throws ends up
>> in the generic perror_with_name today, after the series, they'll
>> end up in throw_ptrace_error instead, a single place we can add
>> more context info to the error thrown.  How is that a bad thing?
> 
> The problem is that you'll always end up losing some context by using
> a throw/catch model.  I think that should be avoided whenever
> possible, and I got the impression that in the specific race you were
> trying to fix here it could be avoided.

If from local context at the ptrace call site the error can be handled
better, sure, but then we wouldn't be calling perror_with_name.  But
the situation is the reverse here.  The context is higher up on the
call chain, not around the ptrace call site.  It's because errors can't
be handled locally that we're _already_ throwing today.  The fix was for all
those ptrace calls deep down in the call chain that can't tell why they're
being called, and so don't know whether they should ignore the error
or not.  For example, say you're poking a register manually in the command
line and that errors out with ESRCH.  Should we ignore that error?  Probably not.
It should probably propagate to the command line.  OTOH, if GDB decides to
flush registers just while it is resuming a thread, and the thread/process
becomes zombie.  We should probably ignore the ESRCH, cancel whatever other
setup we were doing for the resume / skip the remainder of the ptrace calls,
and go straight to collecting the wait status.  In both cases, the registers
write ptrace call is the same.  So now what?

> It also strikes me as undesitable having to add a new an dfairly
> generic file to the list in config/*.mh.

Let me say that objecting on grounds of "new" and "generic" seems
pretty groundless.  ;-)  The file was added to all targets that
already list inf-ptrace.o as first approximation, well because
they'd all need it.  It's a separate file because it's needed
by gdbserver as well.  Adding it to gdbserver was much
simpler given gdbserver/configure.srv though.  What would you
suggest?

Anyway, I've got a better objection to my own patch! :-)

Not against using try/catch, but against a new error code.

I poked a little at making the core behave better in this
whole disappearing process/thread scenario.  A new error code
doesn't really work nicely with remote targets given that
RSP error codes aren't well defined.

So instead, I switched to catching any error and then checking
whether the LWP is now "dead/zombie" by reading its status
in /proc/PID/status.  If so, ignore the error, otherwise, rethrow.
With a scheme like this, there's a tiny race window where we could
have gotten a different error and then the process died, but I
don't think that matters.

If we carry on fixing core GDB to cope better with a disappearing
process, then a scheme like this should work in the core side
too.  We'd use target_thread_alive and/or add a new
target_thread_is_zombie or some such.

So here's my current patch.  It's entirely Linux-specific now.

Let me know what you think.

----
From 2042d98af96655f04d69a0bbc78f0b0a4e203ae1 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Wed, 11 Mar 2015 15:20:31 +0000
Subject: [PATCH] Fix race exposed by gdb.threads/killed.exp

On GNU/Linux, this test sometimes FAILs like this:

 (gdb) run
 Starting program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/killed
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib64/libthread_db.so.1".
 ptrace: No such process.
 (gdb)
 Program terminated with signal SIGKILL, Killed.
 The program no longer exists.
 FAIL: gdb.threads/killed.exp: run program to completion (timeout)

Note the suspicious "No such process" line (that's errno==ESRCH).
Adding debug output we see:

  linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Stopped (signal) (stopped)
  LNW: waitpid(-1, ...) returned 18461, ERRNO-OK
  LLW: waitpid 18461 received Trace/breakpoint trap (stopped)
  LLW: Handling extended status 0x03057f
  LHEW: Got clone event from LWP 18461, new child is LWP 18465
  LNW: waitpid(-1, ...) returned 0, ERRNO-OK
  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0
  sigchld
  ptrace: No such process.
  (gdb) linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Killed (terminated)
  LLW: LWP 18465 exited.
  LNW: waitpid(-1, ...) returned 18461, No child processes
  LLW: waitpid 18461 received Killed (terminated)
  Process 18461 exited
  LNW: waitpid(-1, ...) returned -1, No child processes
  LLW: exit
  sigchld
  infrun: target_wait (-1, status) =
  infrun:   18461 [process 18461],
  infrun:   status->kind = signalled, signal = GDB_SIGNAL_KILL
  infrun: TARGET_WAITKIND_SIGNALLED

  Program terminated with signal SIGKILL, Killed.
  The program no longer exists.
  infrun: stop_waiting
  FAIL: gdb.threads/killed.exp: run program to completion (timeout)

The ptrace call that fails was a PTRACE_CONTINUE.

The issue is that here:

  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0

The first line shows we had just resumed LWP 18465, which does:

 void *
 child_func (void *dummy)
 {
   kill (pid, SIGKILL);
   exit (1);
 }

So if the kernel manages to schedule that thread fast enough, the
process may be killed before GDB has a chance to resume LWP 18461.

GDBserver has code at the tail end of linux_resume_one_lwp to cope
with this:

~~~
    ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
	    (PTRACE_TYPE_ARG3) 0,
	    /* Coerce to a uintptr_t first to avoid potential gcc warning
	       of coercing an 8 byte integer to a 4 byte pointer.  */
	    (PTRACE_TYPE_ARG4) (uintptr_t) signal);

    current_thread = saved_thread;
    if (errno)
      {
	/* ESRCH from ptrace either means that the thread was already
	   running (an error) or that it is gone (a race condition).  If
	   it's gone, we will get a notification the next time we wait,
	   so we can ignore the error.  We could differentiate these
	   two, but it's tricky without waiting; the thread still exists
	   as a zombie, so sending it signal 0 would succeed.  So just
	   ignore ESRCH.  */
	if (errno == ESRCH)
	  return;

	perror_with_name ("ptrace");
      }
~~~

However, that's not a complete fix, because between starting to handle
the resume request and getting that PTRACE_CONTINUE, we run other
ptrace calls that can also fail with ESRCH, and that end up throwing
an error (with perror_with_name).

Whether to ignore ptrace errors or not depends on context that is only
available somewhere up the call chain.  So the fix is to let ptrace
errors throw as they do today, and wrap the resume request in a
TRY/CATCH that swallows it iff the lwp that we were trying to resume
is now zombie.

gdb/gdbserver/ChangeLog:
2015-03-11  Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call perror_with_name.
	(handle_zombie_lwp): New function.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows errors if the LWP is
	zombie.

gdb/ChangeLog:
2015-03-11  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call perror_with_name.
	(handle_zombie_lwp): New function.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows errors if the LWP is
	zombie.
---
 gdb/gdbserver/linux-low.c | 59 +++++++++++++++++++++++++++++++++++------------
 gdb/linux-nat.c           | 43 +++++++++++++++++++++++++++++++++-
 2 files changed, 86 insertions(+), 16 deletions(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 48d905b..e8a66a3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3378,13 +3378,12 @@ stop_all_lwps (int suspend, struct lwp_info *except)
     }
 }
 
-/* Resume execution of the inferior process.
-   If STEP is nonzero, single-step it.
-   If SIGNAL is nonzero, give it that signal.  */
+/* Resume execution of LWP.  If STEP is nonzero, single-step it.  If
+   SIGNAL is nonzero, give it that signal.  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lwp,
-		      int step, int signal, siginfo_t *info)
+linux_resume_one_lwp_throw (struct lwp_info *lwp,
+			    int step, int signal, siginfo_t *info)
 {
   struct thread_info *thread = get_lwp_thread (lwp);
   struct thread_info *saved_thread;
@@ -3576,19 +3575,49 @@ linux_resume_one_lwp (struct lwp_info *lwp,
 
   current_thread = saved_thread;
   if (errno)
+    perror_with_name ("resuming thread");
+}
+
+/* Called when we try to resume a stopped LWP and that errors out.  If
+   the LWP is zombie, discard the error, clear any pending status the
+   LWP may have, and return true (we'll collect the exit status soon
+   enough).  Otherwise, return false.  */
+
+static int
+handle_zombie_lwp (struct lwp_info *lp)
+{
+  struct thread_info *thread = get_lwp_thread (lp);
+
+  if (linux_proc_pid_is_zombie (lwpid_of (thread)))
     {
-      /* ESRCH from ptrace either means that the thread was already
-	 running (an error) or that it is gone (a race condition).  If
-	 it's gone, we will get a notification the next time we wait,
-	 so we can ignore the error.  We could differentiate these
-	 two, but it's tricky without waiting; the thread still exists
-	 as a zombie, so sending it signal 0 would succeed.  So just
-	 ignore ESRCH.  */
-      if (errno == ESRCH)
-	return;
+      lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+      lp->status_pending_p = 0;
+      lp->stopped = 0;
+      return 1;
+    }
+  return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+   disappears while we try to resume it.  */
 
-      perror_with_name ("ptrace");
+static void
+linux_resume_one_lwp (struct lwp_info *lwp,
+		      int step, int signal, siginfo_t *info)
+{
+  TRY
+    {
+      linux_resume_one_lwp_throw (lwp, step, signal, info);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* If we got an error because the thread/process is gone, ignore
+	 it; we will collect the exit status the next time we wait,
+	 shortly.  */
+      if (!handle_zombie_lwp (lwp))
+	throw_exception (ex);
     }
+  END_CATCH
 }
 
 struct thread_resume_array
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 22ce842..1291ab3 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1503,7 +1503,8 @@ linux_nat_detach (struct target_ops *ops, const char *args, int from_tty)
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+linux_resume_one_lwp_throw (struct lwp_info *lp, int step,
+			    enum gdb_signal signo)
 {
   lp->step = step;
 
@@ -1527,6 +1528,46 @@ linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
   registers_changed_ptid (lp->ptid);
 }
 
+/* Called when we try to resume a stopped LWP and that errors out.  If
+   the LWP is zombie, discard the error, clear any pending status the
+   LWP may have, and return true (we'll collect the exit status soon
+   enough).  Otherwise, return false.  */
+
+static int
+handle_zombie_lwp (struct lwp_info *lp)
+{
+  if (linux_proc_pid_is_zombie (ptid_get_lwp (lp->ptid)))
+    {
+      lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+      lp->status = 0;
+      lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+      lp->stopped = 0;
+      return 1;
+    }
+  return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+   disappears while we try to resume it.  */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+{
+  TRY
+    {
+      linux_resume_one_lwp_throw (lp, step, signo);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      /* If we got an error because the thread/process is gone, ignore
+	 it; we will collect the exit status the next time we wait,
+	 shortly.  */
+      if (!handle_zombie_lwp (lp))
+	throw_exception (ex);
+    }
+  END_CATCH
+}
+
 /* Resume LP.  */
 
 static void
-- 
1.9.3

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [pushed] native/Linux: internal error if resume is short-circuited (Re: [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint)
  2015-03-06 19:58 ` [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint Pedro Alves
@ 2015-03-19 12:37   ` Pedro Alves
  2015-03-19 12:49     ` [pushed] gdbserver/Linux: unbreak thread event randomization (Re: [pushed] native/Linux: internal error if resume is short-circuited) Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 12:37 UTC (permalink / raw)
  To: gdb-patches

On 03/06/2015 07:58 PM, Pedro Alves wrote:
> If the inferior disappears just after it was stopped at a breakpoint,
> GDB internal errors on next resume:
> 
>  Executing on target: kill -9 11605    (timeout = 300)
>  spawn -ignore SIGHUP kill -9 11605
>  continue
>  Continuing.
>  /home/pedro/gdb/mygit/src/gdb/linux-nat.c:2590: internal-error: status_callback: Assertion `lp->status != 0' failed.
> 
> This is because the thread had stopped for a breakpoint, and had
> already reported the event, so its ->status flag was cleared.  The
> lwp's stopped, etc., flags should only be cleared when we're sure the
> LWP was successfully resumed (see PR gdb/15713, git 8817a6f2).  So the
> next resume hits an ESRCH error which throws before those flags are
> cleared.  GDB core prints the error, and ends up calling target_wait
> to poll remaining events.  We then trip on the assertion.
> 
> Fix this by bailing out earlier.  GDBserver is already doing this.
> 
> A follow up patch will add a test that exercises this
> (gdb.base/killed-outside.exp).

So I thought some more about this and realized that it's
possible to construct a test case that triggers the assertion,
even without the special-case of a process that disappears.  So I
wrote that test, rewrote the git commit log in that direction,
and pushed it in, as below.

----
From eb54c8bf087f434b0cb91b35e7cde68a69ac9193 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 19 Mar 2015 12:20:25 +0000
Subject: [PATCH] native/Linux: internal error if resume is short-circuited

If the linux_nat_resume's short-circuits the resume because the
current thread has a pending status, and, a thread with a higher
number was previously stopped for a breakpoint, GDB internal errors,
like:

 /home/pedro/gdb/mygit/src/gdb/linux-nat.c:2590: internal-error: status_callback: Assertion `lp->status != 0' failed.

Fix this by make status_callback bail out earlier.  GDBserver is
already doing the same.

New test added that exercises this.

gdb/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (status_callback): Return early if the LWP has no
	status pending.

gdb/testsuite/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* gdb.threads/continue-pending-status.c: New file.
	* gdb.threads/continue-pending-status.exp: New file.
---
 gdb/ChangeLog                                      |   5 +
 gdb/testsuite/ChangeLog                            |   5 +
 gdb/linux-nat.c                                    |   8 +-
 .../gdb.threads/continue-pending-status.c          |  58 +++++++++++
 .../gdb.threads/continue-pending-status.exp        | 110 +++++++++++++++++++++
 5 files changed, 182 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.threads/continue-pending-status.c
 create mode 100644 gdb/testsuite/gdb.threads/continue-pending-status.exp

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 45e5029..7ae3c58 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,10 @@
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
+	* linux-nat.c (status_callback): Return early if the LWP has no
+	status pending.
+
+2015-03-19  Pedro Alves  <palves@redhat.com>
+
 	* linux-nat.c (select_event_lwp_callback): Update comment to no
 	longer mention SIGTRAP.
 
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index d9f4ecf..94dae82 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2015-03-19  Pedro Alves  <palves@redhat.com>
+
+	* gdb.threads/continue-pending-status.c: New file.
+	* gdb.threads/continue-pending-status.exp: New file.
+
 2015-03-18  Pedro Alves  <palves@redhat.com>
 
 	* gdb.base/disp-step-syscall.exp (disp_step_cross_syscall):
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index f5622ac..f5f92d9 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2543,6 +2543,9 @@ status_callback (struct lwp_info *lp, void *data)
   if (!lp->resumed)
     return 0;
 
+  if (!lwp_status_pending_p (lp))
+    return 0;
+
   if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
       || lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
     {
@@ -2551,8 +2554,6 @@ status_callback (struct lwp_info *lp, void *data)
       CORE_ADDR pc;
       int discard = 0;
 
-      gdb_assert (lp->status != 0);
-
       pc = regcache_read_pc (regcache);
 
       if (pc != lp->stop_pc)
@@ -2590,10 +2591,9 @@ status_callback (struct lwp_info *lp, void *data)
 	  linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
 	  return 0;
 	}
-      return 1;
     }
 
-  return lwp_status_pending_p (lp);
+  return 1;
 }
 
 /* Return non-zero if LP isn't stopped.  */
diff --git a/gdb/testsuite/gdb.threads/continue-pending-status.c b/gdb/testsuite/gdb.threads/continue-pending-status.c
new file mode 100644
index 0000000..eea0c7e
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/continue-pending-status.c
@@ -0,0 +1,58 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2015 Free Software Foundation, Inc.
+
+   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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+
+pthread_barrier_t barrier;
+
+#define NUM_THREADS 2
+
+void *
+thread_function (void *arg)
+{
+  /* This ensures that the breakpoint is only hit after both threads
+     are created, so the test can always switch to the non-event
+     thread when the breakpoint triggers.  */
+  pthread_barrier_wait (&barrier);
+
+  while (1); /* break here */
+}
+
+int
+main (void)
+{
+  int i;
+
+  pthread_barrier_init (&barrier, NULL, NUM_THREADS);
+
+  for (i = 0; i < NUM_THREADS; i++)
+    {
+      pthread_t thread;
+      int res;
+
+      res = pthread_create (&thread, NULL,
+			    thread_function, NULL);
+      assert (res == 0);
+    }
+
+  sleep (300);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.threads/continue-pending-status.exp b/gdb/testsuite/gdb.threads/continue-pending-status.exp
new file mode 100644
index 0000000..ff73ce4
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/continue-pending-status.exp
@@ -0,0 +1,110 @@
+# Copyright (C) 2015 Free Software Foundation, Inc.
+
+# 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/>.
+
+# This test exercises the case of stopping for a breakpoint hit of one
+# thread, then switching to a thread that has a status pending and
+# continuing.
+
+standard_testfile
+
+if [prepare_for_testing "failed to prepare" $testfile $srcfile {debug pthreads}] {
+    return -1
+}
+
+if ![runto_main] {
+    untested "could not run to main"
+    return -1
+}
+
+set break_line [gdb_get_line_number "break here"]
+
+# Return current thread's number.
+
+proc get_current_thread {} {
+    global gdb_prompt
+
+    set thread ""
+    set msg "get thread number"
+    gdb_test_multiple "print /x \$_thread" $msg {
+	-re "\\$\[0-9\]* = (0x\[0-9a-zA-Z\]+).*$gdb_prompt $" {
+	    set thread $expect_out(1,string)
+	    pass "$msg"
+	}
+    }
+    return ${thread}
+}
+
+# There are two threads in the program that are running the same tight
+# loop, where we place a breakpoint.  Sometimes we'll get a breakpoint
+# trigger for thread 2, with the breakpoint event of thread 3 pending,
+# other times the opposite.  The original bug that motivated this test
+# depended on the event thread being the highest numbered thread.  We
+# try the same multiple times, which should cover both threads
+# reporting the event.
+
+set attempts 20
+
+for {set i 0} {$i < $attempts} {incr i} {
+    with_test_prefix "attempt $i" {
+	gdb_test "b $srcfile:$break_line" \
+	    "Breakpoint .* at .*$srcfile, line $break_line.*" \
+	    "set break in tight loop"
+	gdb_test "continue" \
+	    "$srcfile:$break_line.*" \
+	    "continue to tight loop"
+
+	# Switch to the thread that did _not_ report the event (and
+	# thus may have a pending status).  At the time this test was
+	# written this was necessary to make linux-nat.c short-circuit
+	# the resume and go straight to consuming the pending event.
+	set thread [get_current_thread]
+	if {$thread == 2} {
+	    set thread 3
+	} else {
+	    set thread 2
+	}
+	gdb_test "thread $thread" \
+	    "Switching to thread $thread .*" \
+	    "switch to non-event thread"
+
+	# Delete all breakpoints so that continuing doesn't switch
+	# back to the event thread to do a step-over, which would mask
+	# away the original bug, which depended on the event thread
+	# still having TARGET_STOPPED_BY_SW_BREAKPOINT stop_reason.
+	delete_breakpoints
+
+	# In the original bug, continuing would trigger an internal
+	# error in the linux-nat.c backend.
+
+	set msg "continue for ctrl-c"
+	gdb_test_multiple "continue" $msg {
+	    -re "Continuing" {
+		pass $msg
+	    }
+	}
+
+	# Wait a bit for GDB to give the terminal to the inferior,
+	# otherwise ctrl-c too soon can result in a "Quit".
+	sleep 1
+	send_gdb "\003"
+
+	set msg "caught interrupt"
+	gdb_test_multiple "" $msg {
+	    -re "Program received signal SIGINT.*$gdb_prompt $" {
+		pass $msg
+	    }
+	}
+    }
+}
-- 
1.9.3


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [pushed] gdbserver/Linux: unbreak thread event randomization (Re: [pushed] native/Linux: internal error if resume is short-circuited)
  2015-03-19 12:37   ` [pushed] native/Linux: internal error if resume is short-circuited (Re: [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint) Pedro Alves
@ 2015-03-19 12:49     ` Pedro Alves
  2015-03-19 16:14       ` [PATCH] gdbserver/Linux: Unbreak non-stop (Re: [pushed] gdbserver/Linux: unbreak thread event randomization) Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 12:49 UTC (permalink / raw)
  To: gdb-patches

On 03/19/2015 12:36 PM, Pedro Alves wrote:

> So I thought some more about this and realized that it's
> possible to construct a test case that triggers the assertion,
> even without the special-case of a process that disappears.  So I
> wrote that test, rewrote the git commit log in that direction,
> and pushed it in, as below.

When I first wrote that patch, I added an extra assert that
surprisingly failed against gdbserver.  It caught a bug straight
from the OMG-can't-believe-this-wasnt-caught-before department...

I've pushed in the gdbserver fix and the extra test assert, as below.

I've made the test dump the counters to the logs, so I can keep an
eye on the buildbots, to make sure the test isn't at real risk of
being racy in practice.

---
From 8bf3b159e55b42bb084f9da1af400a285025618f Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Sun, 15 Mar 2015 19:35:26 +0000
Subject: [PATCH] gdbserver/Linux: unbreak thread event randomization

Wanting to make sure the new continue-pending-status.exp test tests
both cases of threads 2 and 3 reporting an event, I added counters to
the test, to make it FAIL if events for both threads aren't seen.
Assuming a well behaved backend, and given a reasonable number of
iterations, it should PASS.

However, running that against GNU/Linux gdbserver, I found that
surprisingly, that FAILed.  GDBserver always reported the breakpoint
hit for the same thread.

Turns out that I broke gdbserver's thread event randomization
recently, with git commit 582511be ([gdbserver] linux-low.c: better
starvation avoidance, handle non-stop mode too).  In that commit I
missed that the thread structure also has a status_pending_p field...
The end result was that count_events_callback always returns 0, and
then if no thread is stepping, select_event_lwp always returns the
event thread.  IOW, no randomization is happening at all.  Quite
curious how all the other changes in that patch were sufficient to fix
non-stop-fair-events.exp anyway even with that broken.

Tested on x86_64 Fedora 20, native and gdbserver.

gdb/gdbserver/ChangeLog:
2015-03-19 Pedro Alves  <palves@redhat.com>

	* linux-low.c (count_events_callback, select_event_lwp_callback):
	Use the lwp's status_pending_p field, not the thread's.

gdb/testsuite/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* gdb.threads/continue-pending-status.exp (saw_thread_2)
	(saw_thread_3): New globals.
	(top level): Increment them when an event for the corresponding
	thread is seen.
	(no thread starvation): New test.
---
 gdb/ChangeLog                                         |  5 +++++
 gdb/gdbserver/ChangeLog                               |  5 +++++
 gdb/testsuite/ChangeLog                               |  8 ++++++++
 gdb/gdbserver/linux-low.c                             |  7 +++++--
 gdb/linux-nat.c                                       |  1 +
 gdb/testsuite/gdb.threads/continue-pending-status.exp | 14 ++++++++++++++
 6 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 7ae3c58..cfbd576 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2015-03-19 Pedro Alves  <palves@redhat.com>
+
+	* linux-low.c (count_events_callback, select_event_lwp_callback):
+	Use the lwp's status_pending_p field, not the thread's.
+
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
 	* linux-nat.c (status_callback): Return early if the LWP has no
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 6efab5b..df8f9ab 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,8 @@
+2015-03-19 Pedro Alves  <palves@redhat.com>
+
+	* linux-low.c (count_events_callback, select_event_lwp_callback):
+	Use the lwp's status_pending_p field, not the thread's.
+
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
 	* linux-low.c (select_event_lwp_callback): Update comments to
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 94dae82..6bf008a 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,5 +1,13 @@
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
+	* gdb.threads/continue-pending-status.exp (saw_thread_2)
+	(saw_thread_3): New globals.
+	(top level): Increment them when an event for the corresponding
+	thread is seen.
+	(no thread starvation): New test.
+
+2015-03-19  Pedro Alves  <palves@redhat.com>
+
 	* gdb.threads/continue-pending-status.c: New file.
 	* gdb.threads/continue-pending-status.exp: New file.
 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9558f46..e53e0fc 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2238,6 +2238,7 @@ static int
 count_events_callback (struct inferior_list_entry *entry, void *data)
 {
   struct thread_info *thread = (struct thread_info *) entry;
+  struct lwp_info *lp = get_thread_lwp (thread);
   int *count = data;
 
   gdb_assert (count != NULL);
@@ -2245,7 +2246,7 @@ count_events_callback (struct inferior_list_entry *entry, void *data)
   /* Count only resumed LWPs that have an event pending. */
   if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
       && thread->last_resume_kind != resume_stop
-      && thread->status_pending_p)
+      && lp->status_pending_p)
     (*count)++;
 
   return 0;
@@ -2273,6 +2274,7 @@ static int
 select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
 {
   struct thread_info *thread = (struct thread_info *) entry;
+  struct lwp_info *lp = get_thread_lwp (thread);
   int *selector = data;
 
   gdb_assert (selector != NULL);
@@ -2280,7 +2282,7 @@ select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
   /* Select only resumed LWPs that have an event pending. */
   if (thread->last_resume_kind != resume_stop
       && thread->last_status.kind == TARGET_WAITKIND_IGNORE
-      && thread->status_pending_p)
+      && lp->status_pending_p)
     if ((*selector)-- == 0)
       return 1;
 
@@ -2324,6 +2326,7 @@ select_event_lwp (struct lwp_info **orig_lp)
 
       /* First see how many events we have.  */
       find_inferior (&all_threads, count_events_callback, &num_events);
+      gdb_assert (num_events > 0);
 
       /* Now randomly pick a LWP out of those that have had
 	 events.  */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index f5f92d9..40f1e1f 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2841,6 +2841,7 @@ select_event_lwp (ptid_t filter, struct lwp_info **orig_lp, int *status)
 
       /* First see how many events we have.  */
       iterate_over_lwps (filter, count_events_callback, &num_events);
+      gdb_assert (num_events > 0);
 
       /* Now randomly pick a LWP out of those that have had
 	 events.  */
diff --git a/gdb/testsuite/gdb.threads/continue-pending-status.exp b/gdb/testsuite/gdb.threads/continue-pending-status.exp
index ff73ce4..1f170f7 100644
--- a/gdb/testsuite/gdb.threads/continue-pending-status.exp
+++ b/gdb/testsuite/gdb.threads/continue-pending-status.exp
@@ -56,6 +56,13 @@ proc get_current_thread {} {
 
 set attempts 20
 
+# These track whether we saw events for both threads 2 and 3.  If the
+# backend always returns the breakpoint hit for the same thread, then
+# it fails to make sure threads aren't starved, and we'll fail the
+# assert after the loop.
+set saw_thread_2 0
+set saw_thread_3 0
+
 for {set i 0} {$i < $attempts} {incr i} {
     with_test_prefix "attempt $i" {
 	gdb_test "b $srcfile:$break_line" \
@@ -71,8 +78,10 @@ for {set i 0} {$i < $attempts} {incr i} {
 	# the resume and go straight to consuming the pending event.
 	set thread [get_current_thread]
 	if {$thread == 2} {
+	    incr saw_thread_2
 	    set thread 3
 	} else {
+	    incr saw_thread_3
 	    set thread 2
 	}
 	gdb_test "thread $thread" \
@@ -108,3 +117,8 @@ for {set i 0} {$i < $attempts} {incr i} {
 	}
     }
 }
+
+verbose -log "saw_thread_2=$saw_thread_2"
+verbose -log "saw_thread_3=$saw_thread_3"
+
+gdb_assert {$saw_thread_2 > 0 && $saw_thread_3 > 0} "no thread starvation"
-- 
1.9.3


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH] gdbserver/Linux: Unbreak non-stop (Re: [pushed] gdbserver/Linux: unbreak thread event randomization)
  2015-03-19 12:49     ` [pushed] gdbserver/Linux: unbreak thread event randomization (Re: [pushed] native/Linux: internal error if resume is short-circuited) Pedro Alves
@ 2015-03-19 16:14       ` Pedro Alves
  2015-03-19 16:54         ` [pushed] " Pedro Alves
  0 siblings, 1 reply; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 16:14 UTC (permalink / raw)
  To: gdb-patches

On 03/19/2015 12:49 PM, Pedro Alves wrote:

> However, running that against GNU/Linux gdbserver, I found that
> surprisingly, that FAILed.  GDBserver always reported the breakpoint
> hit for the same thread.
> 
> Turns out that I broke gdbserver's thread event randomization
> recently, with git commit 582511be ([gdbserver] linux-low.c: better
> starvation avoidance, handle non-stop mode too).  In that commit I
> missed that the thread structure also has a status_pending_p field...
> The end result was that count_events_callback always returns 0, and
> then if no thread is stepping, select_event_lwp always returns the
> event thread.  IOW, no randomization is happening at all.  Quite
> curious how all the other changes in that patch were sufficient to fix
> non-stop-fair-events.exp anyway even with that broken.

... and now the buildbots caught another bug in the same functions.

Rerunning the tests locally, I can see it trigger too.
Testing this fix now.

---
From aef94259924021e912d3c4a35fbdde7c767f0c49 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 19 Mar 2015 15:59:33 +0000
Subject: [PATCH] gdbserver/Linux: Unbreak non-stop

The previous change added an assertion that is catching yet another
bug in count_events_callback/select_event_lwp_callback:

  (gdb)
  PASS: gdb.mi/mi-nonstop.exp: interrupted
  mi_expect_interrupt: expecting: \*stopped,(reason="signal-received",signal-name="0",signal-meaning="Signal 0"|reason="signal-received",signal-name="SIGINT",signal-meaning="Interrupt")[^
  ]*

  /home/pedro/gdb/mygit/src/gdb/gdbserver/linux-low.c:2329: A problem internal to GDBserver has been detected.
  select_event_lwp: Assertion `num_events > 0' failed.
  =thread-group-exited,id="i1"

Certainly select_event_lwp_callback should always at least find one
event, as it's only called because an event triggered (though we may
have more than one: the point of the function is randomly picking
one).

An LWP that GDB previously asked to continue/step (thus is resumed)
resumed and gets a vCont;t request ends up with last_resume_kind ==
resume_stop.  These functions in gdbserver used to filter out events
that weren't going to be reported to GDB; I think the last_resume_kind
kind check used to make sense at some point, but it no longer does.

gdb/gdbserver/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* linux-low.c (count_events_callback, select_event_lwp_callback):
	No longer check whether the thread has resume_stop as last resume
	kind.
---
 gdb/gdbserver/linux-low.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index e53e0fc..2b988ec 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -2245,7 +2245,6 @@ count_events_callback (struct inferior_list_entry *entry, void *data)
 
   /* Count only resumed LWPs that have an event pending. */
   if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
-      && thread->last_resume_kind != resume_stop
       && lp->status_pending_p)
     (*count)++;
 
@@ -2280,8 +2279,7 @@ select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
   gdb_assert (selector != NULL);
 
   /* Select only resumed LWPs that have an event pending. */
-  if (thread->last_resume_kind != resume_stop
-      && thread->last_status.kind == TARGET_WAITKIND_IGNORE
+  if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
       && lp->status_pending_p)
     if ((*selector)-- == 0)
       return 1;
-- 
1.9.3


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [pushed] Re: [PATCH] gdbserver/Linux: Unbreak non-stop (Re: [pushed] gdbserver/Linux: unbreak thread event randomization)
  2015-03-19 16:14       ` [PATCH] gdbserver/Linux: Unbreak non-stop (Re: [pushed] gdbserver/Linux: unbreak thread event randomization) Pedro Alves
@ 2015-03-19 16:54         ` Pedro Alves
  0 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 16:54 UTC (permalink / raw)
  To: gdb-patches

On 03/19/2015 04:14 PM, Pedro Alves wrote:

> 
> ... and now the buildbots caught another bug in the same functions.
> 
> Rerunning the tests locally, I can see it trigger too.
> Testing this fix now.

Alright, I ran the testsuite twice against gdbserver, and all
looked good here.  I pushed the fix in.

> From aef94259924021e912d3c4a35fbdde7c767f0c49 Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Thu, 19 Mar 2015 15:59:33 +0000
> Subject: [PATCH] gdbserver/Linux: Unbreak non-stop

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [pushed] Fix race exposed by gdb.threads/killed.exp (Re: [PATCH 2/6] Introduce throw_ptrace_error)
  2015-03-11 15:44             ` Pedro Alves
@ 2015-03-19 17:33               ` Pedro Alves
  0 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 17:33 UTC (permalink / raw)
  To: Mark Kettenis; +Cc: gdb-patches

On 03/11/2015 03:44 PM, Pedro Alves wrote:

> If from local context at the ptrace call site the error can be handled
> better, sure, but then we wouldn't be calling perror_with_name.  But
> the situation is the reverse here.  The context is higher up on the
> call chain, not around the ptrace call site.  It's because errors can't
> be handled locally that we're _already_ throwing today.  The fix was for all
> those ptrace calls deep down in the call chain that can't tell why they're
> being called, and so don't know whether they should ignore the error
> or not.  For example, say you're poking a register manually in the command
> line and that errors out with ESRCH.  Should we ignore that error?  Probably not.
> It should probably propagate to the command line.  OTOH, if GDB decides to
> flush registers just while it is resuming a thread, and the thread/process
> becomes zombie.  We should probably ignore the ESRCH, cancel whatever other
> setup we were doing for the resume / skip the remainder of the ptrace calls,
> and go straight to collecting the wait status.  In both cases, the registers
> write ptrace call is the same.  So now what?

Turns out that the killed.exp test was still racy, exactly because
of a code path in the backend like that.

...
LLW: waitpid 4212 received Trace/breakpoint trap (stopped)
LLW: Handling extended status 0x03057f
LHEW: Got clone event from LWP 4212, new child is LWP 4216
[New Thread 0x7ffff7fc0700 (LWP 4216)]
LNW: waitpid(-1, ...) returned 0, ERRNO-OK
RSRL: resuming stopped-resumed LWP Thread 0x7ffff7fc0700 (LWP 4216) at 0x3615ef4ce1: step=0
RSRL: resuming stopped-resumed LWP Thread 0x7ffff7fc1740 (LWP 4212) at 0x3615ef4ce1: step=0
sigchld
ptrace: No such process.
(gdb) linux_nat_wait: [process -1], [TARGET_WNOHANG]
LLW: enter
sigchld
...

This time the error was thrown while GDB was trying to read the
PC of the LWP that is about to be resumed, in resume_stopped_resumed_lwps.
Fixed that with another TRY/CATCH.

And, it turns out that version of the fix that reads the status
from /proc/pid/status was still racy as is too.  I was still seeing
"ptrace: No such process" sometimes.  That was because there's a
tiny race window where ptrace fails with ESRCH, but /proc/pid/status
shows a state different from zombie.  I hacked gdb to print the
state, and I saw:

RSRL: resuming stopped-resumed LWP Thread 0x7ffff7fc0700 (LWP 9947) at 0x3615ef4ce1: step=0
RSRL: resuming stopped-resumed LWP Thread 0x7ffff7fc1740 (LWP 9943) at 0x3615ef4ce1: step=0
infrun: state line of pid=9943 is "State:    R (running)" (have_state=1)
sigchld
sigchld
ptrace: No such process (inferior_ptid=[Thread 0x7ffff7fc1740 (LWP 9943)]).

This is the same as mentioned in the ptrace(2) man page:

  The  tracer  cannot assume that the ptrace-stopped tracee exists.
  There are many scenarios when the tracee may die while stopped 
  (such as SIGKILL).  Therefore, the tracer must be prepared to handle an
  ESRCH error on any ptrace operation.  Unfortunately, the same error is 
  returned if the tracee exists but is not ptrace-stopped (for commands
  which require a stopped tracee), or if it is not traced by the process
  which issued the ptrace call.  The tracer needs to keep track of
  the stopped/running state of the tracee, and interpret ESRCH as "tracee died
  unexpectedly" only if it knows that the tracee has been observed to
  enter  ptrace-stop.   Note that there is no guarantee that waitpid(WNOHANG)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  will reliably report the tracee's death status if a ptrace operation
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  returned ESRCH.  waitpid(WNOHANG) may return 0 instead.  In other words,
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  the tracee may be "not yet fully dead", but already refusing ptrace requests.
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

We already keep track of stopped/running state.  So instead of checking whether
/proc/pid/status shows "Z (Zombie)" state, I'm checking the opposite -- whether
the lwp is NOT in "T (tracing stop)" state (that is, no longer ptrace-stopped).

Here's what I pushed.

---
From 23f238d3456531db33456918f004dcc5ce151363 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 19 Mar 2015 15:12:33 +0000
Subject: [PATCH] Fix race exposed by gdb.threads/killed.exp

On GNU/Linux, this test sometimes FAILs like this:

 (gdb) run
 Starting program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/killed
 [Thread debugging using libthread_db enabled]
 Using host libthread_db library "/lib64/libthread_db.so.1".
 ptrace: No such process.
 (gdb)
 Program terminated with signal SIGKILL, Killed.
 The program no longer exists.
 FAIL: gdb.threads/killed.exp: run program to completion (timeout)

Note the suspicious "No such process" line (that's errno==ESRCH).
Adding debug output we see:

  linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Stopped (signal) (stopped)
  LNW: waitpid(-1, ...) returned 18461, ERRNO-OK
  LLW: waitpid 18461 received Trace/breakpoint trap (stopped)
  LLW: Handling extended status 0x03057f
  LHEW: Got clone event from LWP 18461, new child is LWP 18465
  LNW: waitpid(-1, ...) returned 0, ERRNO-OK
  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0
  sigchld
  ptrace: No such process.
  (gdb) linux_nat_wait: [process -1], [TARGET_WNOHANG]
  LLW: enter
  LNW: waitpid(-1, ...) returned 18465, ERRNO-OK
  LLW: waitpid 18465 received Killed (terminated)
  LLW: LWP 18465 exited.
  LNW: waitpid(-1, ...) returned 18461, No child processes
  LLW: waitpid 18461 received Killed (terminated)
  Process 18461 exited
  LNW: waitpid(-1, ...) returned -1, No child processes
  LLW: exit
  sigchld
  infrun: target_wait (-1, status) =
  infrun:   18461 [process 18461],
  infrun:   status->kind = signalled, signal = GDB_SIGNAL_KILL
  infrun: TARGET_WAITKIND_SIGNALLED

  Program terminated with signal SIGKILL, Killed.
  The program no longer exists.
  infrun: stop_waiting
  FAIL: gdb.threads/killed.exp: run program to completion (timeout)

The issue is that here:

  RSRL: resuming stopped-resumed LWP LWP 18465 at 0x3b36af4b51: step=0
  RSRL: resuming stopped-resumed LWP LWP 18461 at 0x3b36af4b51: step=0

The first line shows we had just resumed LWP 18465, which does:

 void *
 child_func (void *dummy)
 {
   kill (pid, SIGKILL);
   exit (1);
 }

So if the kernel manages to schedule that thread fast enough, the
process may be killed before GDB has a chance to resume LWP 18461.

GDBserver has code at the tail end of linux_resume_one_lwp to cope
with this:

~~~
    ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
	    (PTRACE_TYPE_ARG3) 0,
	    /* Coerce to a uintptr_t first to avoid potential gcc warning
	       of coercing an 8 byte integer to a 4 byte pointer.  */
	    (PTRACE_TYPE_ARG4) (uintptr_t) signal);

    current_thread = saved_thread;
    if (errno)
      {
	/* ESRCH from ptrace either means that the thread was already
	   running (an error) or that it is gone (a race condition).  If
	   it's gone, we will get a notification the next time we wait,
	   so we can ignore the error.  We could differentiate these
	   two, but it's tricky without waiting; the thread still exists
	   as a zombie, so sending it signal 0 would succeed.  So just
	   ignore ESRCH.  */
	if (errno == ESRCH)
	  return;

	perror_with_name ("ptrace");
      }
~~~

However, that's not a complete fix, because between starting to handle
the resume request and getting that PTRACE_CONTINUE, we run other
ptrace calls that can also fail with ESRCH, and that end up throwing
an error (with perror_with_name).

In the case above, I indeed sometimes see resume_stopped_resumed_lwps
fail in the registers read:

resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
{
...
      CORE_ADDR pc = regcache_read_pc (regcache);

Or e.g., in 32-bit mode, i386_linux_resume has several calls that can
throw too.

Whether to ignore ptrace errors or not depends on context that is only
available somewhere up the call chain.  So the fix is to let ptrace
errors throw as they do today, and wrap the resume request in a
TRY/CATCH that swallows it iff the lwp that we were trying to resume
is no longer ptrace-stopped.

gdb/gdbserver/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call perror_with_name.
	(check_ptrace_stopped_lwp_gone): New function.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows errors if the LWP is
	gone.

gdb/ChangeLog:
2015-03-19  Pedro Alves  <palves@redhat.com>

	* linux-nat.c (linux_resume_one_lwp): Rename to ...
	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
	instead call perror_with_name.
	(check_ptrace_stopped_lwp_gone): New function.
	(linux_resume_one_lwp): Reimplement as wrapper around
	linux_resume_one_lwp_throw that swallows errors if the LWP is
	gone.
	(resume_stopped_resumed_lwps): Try register reads in TRY/CATCH and
	swallows errors if the LWP is gone.  Use
	linux_resume_one_lwp_throw instead of linux_resume_one_lwp.
---
 gdb/ChangeLog             |  13 ++++++
 gdb/gdbserver/ChangeLog   |  10 +++++
 gdb/gdbserver/linux-low.c |  80 +++++++++++++++++++++++++++--------
 gdb/linux-nat.c           | 105 +++++++++++++++++++++++++++++++++++++++-------
 gdb/nat/linux-procfs.c    |   9 ++++
 gdb/nat/linux-procfs.h    |   2 +
 6 files changed, 186 insertions(+), 33 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 7ae3c58..a8b8850 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,18 @@
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
+	* linux-nat.c (linux_resume_one_lwp): Rename to ...
+	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
+	instead call perror_with_name.
+	(check_ptrace_stopped_lwp_gone): New function.
+	(linux_resume_one_lwp): Reimplement as wrapper around
+	linux_resume_one_lwp_throw that swallows errors if the LWP is
+	gone.
+	(resume_stopped_resumed_lwps): Try register reads in TRY/CATCH and
+	swallows errors if the LWP is gone.  Use
+	linux_resume_one_lwp_throw instead of linux_resume_one_lwp.
+
+2015-03-19  Pedro Alves  <palves@redhat.com>
+
 	* linux-nat.c (status_callback): Return early if the LWP has no
 	status pending.
 
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 0383e67..cbd199b 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,5 +1,15 @@
 2015-03-19  Pedro Alves  <palves@redhat.com>
 
+	* linux-low.c (linux_resume_one_lwp): Rename to ...
+	(linux_resume_one_lwp_throw): ... this.  Don't handle ESRCH here,
+	instead call perror_with_name.
+	(check_ptrace_stopped_lwp_gone): New function.
+	(linux_resume_one_lwp): Reimplement as wrapper around
+	linux_resume_one_lwp_throw that swallows errors if the LWP is
+	gone.
+
+2015-03-19  Pedro Alves  <palves@redhat.com>
+
 	* linux-low.c (count_events_callback, select_event_lwp_callback):
 	No longer check whether the thread has resume_stop as last resume
 	kind.
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 2b988ec..0c54115 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -3379,13 +3379,12 @@ stop_all_lwps (int suspend, struct lwp_info *except)
     }
 }
 
-/* Resume execution of the inferior process.
-   If STEP is nonzero, single-step it.
-   If SIGNAL is nonzero, give it that signal.  */
+/* Resume execution of LWP.  If STEP is nonzero, single-step it.  If
+   SIGNAL is nonzero, give it that signal.  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lwp,
-		      int step, int signal, siginfo_t *info)
+linux_resume_one_lwp_throw (struct lwp_info *lwp,
+			    int step, int signal, siginfo_t *info)
 {
   struct thread_info *thread = get_lwp_thread (lwp);
   struct thread_info *saved_thread;
@@ -3566,8 +3565,6 @@ linux_resume_one_lwp (struct lwp_info *lwp,
 
   regcache_invalidate_thread (thread);
   errno = 0;
-  lwp->stopped = 0;
-  lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
   lwp->stepping = step;
   ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (thread),
 	  (PTRACE_TYPE_ARG3) 0,
@@ -3577,19 +3574,68 @@ linux_resume_one_lwp (struct lwp_info *lwp,
 
   current_thread = saved_thread;
   if (errno)
+    perror_with_name ("resuming thread");
+
+  /* Successfully resumed.  Clear state that no longer makes sense,
+     and mark the LWP as running.  Must not do this before resuming
+     otherwise if that fails other code will be confused.  E.g., we'd
+     later try to stop the LWP and hang forever waiting for a stop
+     status.  Note that we must not throw after this is cleared,
+     otherwise handle_zombie_lwp_error would get confused.  */
+  lwp->stopped = 0;
+  lwp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+}
+
+/* Called when we try to resume a stopped LWP and that errors out.  If
+   the LWP is no longer in ptrace-stopped state (meaning it's zombie,
+   or about to become), discard the error, clear any pending status
+   the LWP may have, and return true (we'll collect the exit status
+   soon enough).  Otherwise, return false.  */
+
+static int
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
+{
+  struct thread_info *thread = get_lwp_thread (lp);
+
+  /* If we get an error after resuming the LWP successfully, we'd
+     confuse !T state for the LWP being gone.  */
+  gdb_assert (lp->stopped);
+
+  /* We can't just check whether the LWP is in 'Z (Zombie)' state,
+     because even if ptrace failed with ESRCH, the tracee may be "not
+     yet fully dead", but already refusing ptrace requests.  In that
+     case the tracee has 'R (Running)' state for a little bit
+     (observed in Linux 3.18).  See also the note on ESRCH in the
+     ptrace(2) man page.  Instead, check whether the LWP has any state
+     other than ptrace-stopped.  */
+
+  /* Don't assume anything if /proc/PID/status can't be read.  */
+  if (linux_proc_pid_is_trace_stopped_nowarn (lwpid_of (thread)) == 0)
     {
-      /* ESRCH from ptrace either means that the thread was already
-	 running (an error) or that it is gone (a race condition).  If
-	 it's gone, we will get a notification the next time we wait,
-	 so we can ignore the error.  We could differentiate these
-	 two, but it's tricky without waiting; the thread still exists
-	 as a zombie, so sending it signal 0 would succeed.  So just
-	 ignore ESRCH.  */
-      if (errno == ESRCH)
-	return;
+      lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+      lp->status_pending_p = 0;
+      return 1;
+    }
+  return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+   disappears while we try to resume it.  */
 
-      perror_with_name ("ptrace");
+static void
+linux_resume_one_lwp (struct lwp_info *lwp,
+		      int step, int signal, siginfo_t *info)
+{
+  TRY
+    {
+      linux_resume_one_lwp_throw (lwp, step, signal, info);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      if (!check_ptrace_stopped_lwp_gone (lwp))
+	throw_exception (ex);
     }
+  END_CATCH
 }
 
 struct thread_resume_array
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 40f1e1f..8b62041 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1503,7 +1503,8 @@ linux_nat_detach (struct target_ops *ops, const char *args, int from_tty)
    single-step it.  If SIGNAL is nonzero, give it that signal.  */
 
 static void
-linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+linux_resume_one_lwp_throw (struct lwp_info *lp, int step,
+			    enum gdb_signal signo)
 {
   lp->step = step;
 
@@ -1522,11 +1523,68 @@ linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
   if (linux_nat_prepare_to_resume != NULL)
     linux_nat_prepare_to_resume (lp);
   linux_ops->to_resume (linux_ops, lp->ptid, step, signo);
-  lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+
+  /* Successfully resumed.  Clear state that no longer makes sense,
+     and mark the LWP as running.  Must not do this before resuming
+     otherwise if that fails other code will be confused.  E.g., we'd
+     later try to stop the LWP and hang forever waiting for a stop
+     status.  Note that we must not throw after this is cleared,
+     otherwise handle_zombie_lwp_error would get confused.  */
   lp->stopped = 0;
+  lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
   registers_changed_ptid (lp->ptid);
 }
 
+/* Called when we try to resume a stopped LWP and that errors out.  If
+   the LWP is no longer in ptrace-stopped state (meaning it's zombie,
+   or about to become), discard the error, clear any pending status
+   the LWP may have, and return true (we'll collect the exit status
+   soon enough).  Otherwise, return false.  */
+
+static int
+check_ptrace_stopped_lwp_gone (struct lwp_info *lp)
+{
+  /* If we get an error after resuming the LWP successfully, we'd
+     confuse !T state for the LWP being gone.  */
+  gdb_assert (lp->stopped);
+
+  /* We can't just check whether the LWP is in 'Z (Zombie)' state,
+     because even if ptrace failed with ESRCH, the tracee may be "not
+     yet fully dead", but already refusing ptrace requests.  In that
+     case the tracee has 'R (Running)' state for a little bit
+     (observed in Linux 3.18).  See also the note on ESRCH in the
+     ptrace(2) man page.  Instead, check whether the LWP has any state
+     other than ptrace-stopped.  */
+
+  /* Don't assume anything if /proc/PID/status can't be read.  */
+  if (linux_proc_pid_is_trace_stopped_nowarn (ptid_get_lwp (lp->ptid)) == 0)
+    {
+      lp->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+      lp->status = 0;
+      lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
+      return 1;
+    }
+  return 0;
+}
+
+/* Like linux_resume_one_lwp_throw, but no error is thrown if the LWP
+   disappears while we try to resume it.  */
+
+static void
+linux_resume_one_lwp (struct lwp_info *lp, int step, enum gdb_signal signo)
+{
+  TRY
+    {
+      linux_resume_one_lwp_throw (lp, step, signo);
+    }
+  CATCH (ex, RETURN_MASK_ERROR)
+    {
+      if (!check_ptrace_stopped_lwp_gone (lp))
+	throw_exception (ex);
+    }
+  END_CATCH
+}
+
 /* Resume LP.  */
 
 static void
@@ -3542,24 +3600,39 @@ resume_stopped_resumed_lwps (struct lwp_info *lp, void *data)
     {
       struct regcache *regcache = get_thread_regcache (lp->ptid);
       struct gdbarch *gdbarch = get_regcache_arch (regcache);
-      CORE_ADDR pc = regcache_read_pc (regcache);
 
-      /* Don't bother if there's a breakpoint at PC that we'd hit
-	 immediately, and we're not waiting for this LWP.  */
-      if (!ptid_match (lp->ptid, *wait_ptid_p))
+      TRY
 	{
-	  if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
-	    return 0;
-	}
+	  CORE_ADDR pc = regcache_read_pc (regcache);
+	  int leave_stopped = 0;
 
-      if (debug_linux_nat)
-	fprintf_unfiltered (gdb_stdlog,
-			    "RSRL: resuming stopped-resumed LWP %s at %s: step=%d\n",
-			    target_pid_to_str (lp->ptid),
-			    paddress (gdbarch, pc),
-			    lp->step);
+	  /* Don't bother if there's a breakpoint at PC that we'd hit
+	     immediately, and we're not waiting for this LWP.  */
+	  if (!ptid_match (lp->ptid, *wait_ptid_p))
+	    {
+	      if (breakpoint_inserted_here_p (get_regcache_aspace (regcache), pc))
+		leave_stopped = 1;
+	    }
 
-      linux_resume_one_lwp (lp, lp->step, GDB_SIGNAL_0);
+	  if (!leave_stopped)
+	    {
+	      if (debug_linux_nat)
+		fprintf_unfiltered (gdb_stdlog,
+				    "RSRL: resuming stopped-resumed LWP %s at "
+				    "%s: step=%d\n",
+				    target_pid_to_str (lp->ptid),
+				    paddress (gdbarch, pc),
+				    lp->step);
+
+	      linux_resume_one_lwp_throw (lp, lp->step, GDB_SIGNAL_0);
+	    }
+	}
+      CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  if (!check_ptrace_stopped_lwp_gone (lp))
+	    throw_exception (ex);
+	}
+      END_CATCH
     }
 
   return 0;
diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index f149383..7599b32 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -151,6 +151,15 @@ linux_proc_pid_is_stopped (pid_t pid)
   return linux_proc_pid_has_state (pid, "T (stopped)", 1);
 }
 
+/* Detect `T (tracing stop)' in `/proc/PID/status'.
+   Other states including `T (stopped)' are reported as false.  */
+
+int
+linux_proc_pid_is_trace_stopped_nowarn (pid_t pid)
+{
+  return linux_proc_pid_has_state (pid, "T (tracing stop)", 1);
+}
+
 /* Return non-zero if PID is a zombie.  If WARN, warn on failure to
    open the /proc file.  */
 
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index 979ae0d..c4f5788 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -36,6 +36,8 @@ extern pid_t linux_proc_get_tracerpid_nowarn (pid_t lwpid);
 
 extern int linux_proc_pid_is_stopped (pid_t pid);
 
+extern int linux_proc_pid_is_trace_stopped_nowarn (pid_t pid);
+
 /* Return non-zero if PID is a zombie.  Failure to open the
    /proc/pid/status file results in a warning.  */
 
-- 
1.9.3


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp
  2015-03-06 19:58 ` [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp Pedro Alves
@ 2015-03-19 17:39   ` Pedro Alves
  0 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-03-19 17:39 UTC (permalink / raw)
  To: gdb-patches

The version of this this patch that went in ended up
discussed under patch #2's subthread, at:

 https://sourceware.org/ml/gdb-patches/2015-03/msg00597.html

I suspect we'll need to redo this if/when we want to
handle "thread disappeared" on the core side, but for now,
this is good enough.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB
  2015-03-06 20:27 ` [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB Pedro Alves
@ 2015-07-14 10:22   ` Pedro Alves
  0 siblings, 0 replies; 20+ messages in thread
From: Pedro Alves @ 2015-07-14 10:22 UTC (permalink / raw)
  To: gdb-patches

On 03/06/2015 07:58 PM, Pedro Alves wrote:
> This exercises the case of the inferior disappearing while GDB is
> debugging it, such as something doing "kill -9 PID" while the program
> is stopped under GDB or GDBserver.  This triggered a set of internal
> errors, fixed by the previous patches.
> 
> gdb/testsuite/ChangeLog:
> 2015-03-06  Pedro Alves  <palves@redhat.com>
> 
> 	* gdb.base/killed-outside.exp: New file.
> 	* gdb.base/killed-outside.c: New file.

I pushed this in.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-07-14 10:22 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-06 19:58 [PATCH 0/6] Fix problems if inferior disappears while being debugged Pedro Alves
2015-03-06 19:58 ` [PATCH 3/6] Fix race exposed by gdb.threads/killed.exp Pedro Alves
2015-03-19 17:39   ` Pedro Alves
2015-03-06 19:58 ` [PATCH 1/6] Move throw_perror_with_name to common/ Pedro Alves
2015-03-06 19:58 ` [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint Pedro Alves
2015-03-19 12:37   ` [pushed] native/Linux: internal error if resume is short-circuited (Re: [PATCH 4/6] native/Linux: internal error if inferior disappears after stopped by breakpoint) Pedro Alves
2015-03-19 12:49     ` [pushed] gdbserver/Linux: unbreak thread event randomization (Re: [pushed] native/Linux: internal error if resume is short-circuited) Pedro Alves
2015-03-19 16:14       ` [PATCH] gdbserver/Linux: Unbreak non-stop (Re: [pushed] gdbserver/Linux: unbreak thread event randomization) Pedro Alves
2015-03-19 16:54         ` [pushed] " Pedro Alves
2015-03-06 19:58 ` [PATCH 2/6] Introduce throw_ptrace_error Pedro Alves
2015-03-06 21:04   ` Mark Kettenis
2015-03-06 21:40     ` Pedro Alves
2015-03-08 20:30       ` Mark Kettenis
2015-03-08 21:48         ` Pedro Alves
2015-03-10 14:53           ` Mark Kettenis
2015-03-11 15:44             ` Pedro Alves
2015-03-19 17:33               ` [pushed] Fix race exposed by gdb.threads/killed.exp (Re: [PATCH 2/6] Introduce throw_ptrace_error) Pedro Alves
2015-03-06 19:58 ` [PATCH 5/6] gdbserver/Linux: internal error when killing a process that is already gone Pedro Alves
2015-03-06 20:27 ` [PATCH 6/6] Add test that exercises the inferior being killed while stopped under GDB Pedro Alves
2015-07-14 10:22   ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).