gdb: Replace operator new / operator new[]
authorPedro Alves <palves@redhat.com>
Fri, 23 Sep 2016 15:42:24 +0000 (16:42 +0100)
committerPedro Alves <palves@redhat.com>
Fri, 23 Sep 2016 15:42:24 +0000 (16:42 +0100)
If xmalloc fails allocating memory, usually because something tried a
huge allocation, like xmalloc(-1) or some such, GDB asks the user what
to do:

  .../src/gdb/utils.c:1079: internal-error: virtual memory exhausted.
  A problem internal to GDB has been detected,
  further debugging may prove unreliable.
  Quit this debugging session? (y or n)

If the user says "n", that throws a QUIT exception, which is caught by
one of the multiple CATCH(RETURN_MASK_ALL) blocks somewhere up the
stack.

The default implementations of operator new / operator new[] call
malloc directly, and on memory allocation failure throw
std::bad_alloc.  Currently, if that happens, since nothing catches it,
the exception escapes out of main, and GDB aborts from unhandled
exception.

This patch replaces the default operator new variants with versions
that, just like xmalloc:

 #1 - Raise an internal-error on memory allocation failure.

 #2 - Throw a QUIT gdb_exception, so that the exact same CATCH blocks
      continue handling memory allocation problems.

A minor complication of #2 is that operator new can _only_ throw
std::bad_alloc, or something that extends it:

  void* operator new (std::size_t size) throw (std::bad_alloc);

That means that if we let a gdb QUIT exception escape from within
operator new, the C++ runtime aborts due to unexpected exception
thrown.

So to bridge the gap, this patch adds a new gdb_quit_bad_alloc
exception type that inherits both std::bad_alloc and gdb_exception,
and throws _that_.

If we decide that we should be catching memory allocation errors in
fewer places than all the places we currently catch them (everywhere
we use RETURN_MASK_ALL currently), then we could change operator new
to throw plain std::bad_alloc then.  But I'm considering such a change
as separate matter from this one -- it'd make sense to do the same to
xmalloc at the same time, for instance.

Meanwhile, this allows using new/new[] instead of xmalloc/XNEW/etc.
without losing the "virtual memory exhausted" internal-error
safeguard.

Tested on x86_64 Fedora 23.

gdb/ChangeLog:
2016-09-23  Pedro Alves  <palves@redhat.com>

* Makefile.in (SFILES): Add common/new-op.c.
(COMMON_OBS): Add common/new-op.o.
(new-op.o): New rule.
* common/common-exceptions.h: Include <new>.
(struct gdb_quit_bad_alloc): New type.
* common/new-op.c: New file.

gdb/gdbserver/ChangeLog:
2016-09-23  Pedro Alves  <palves@redhat.com>

* Makefile.in (SFILES): Add common/new-op.c.
(OBS): Add common/new-op.o.
(new-op.o): New rule.

gdb/ChangeLog
gdb/Makefile.in
gdb/common/common-exceptions.h
gdb/common/new-op.c [new file with mode: 0644]
gdb/gdbserver/ChangeLog
gdb/gdbserver/Makefile.in

index ea14f041c02e92f89e0326b270de0d3dc5f40301..3826299bd240fa550ef63ba1acac807411e6e3e1 100644 (file)
@@ -1,3 +1,12 @@
+2016-09-23  Pedro Alves  <palves@redhat.com>
+
+       * Makefile.in (SFILES): Add common/new-op.c.
+       (COMMON_OBS): Add common/new-op.o.
+       (new-op.o): New rule.
+       * common/common-exceptions.h: Include <new>.
+       (struct gdb_quit_bad_alloc): New type.
+       * common/new-op.c: New file.
+
 2016-09-22  Edjunior Barbosa Machado  <emachado@linux.vnet.ibm.com>
 
        * rs6000-tdep.c (ppc_process_record_op31): Fix
index 00de1623fb4483a9907d56792d9fec38a42f334b..d0a968ad7748cc3cf1a89966352e69d383d9eb3f 100644 (file)
@@ -893,7 +893,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \
        target/waitstatus.c common/print-utils.c common/rsp-low.c \
        common/errors.c common/common-debug.c common/common-exceptions.c \
        common/btrace-common.c common/fileio.c common/common-regcache.c \
-       common/signals-state-save-restore.c \
+       common/signals-state-save-restore.c common/new-op.c \
        $(SUBDIR_GCC_COMPILE_SRCS)
 
 LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
@@ -1088,7 +1088,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
        format.o registry.o btrace.o record-btrace.o waitstatus.o \
        print-utils.o rsp-low.o errors.o common-debug.o debug.o \
        common-exceptions.o btrace-common.o fileio.o \
-       common-regcache.o \
+       common-regcache.o new-op.o \
        $(SUBDIR_GCC_COMPILE_OBS)
 
 TSOBS = inflow.o
@@ -2290,6 +2290,10 @@ signals-state-save-restore.o: $(srcdir)/common/signals-state-save-restore.c
        $(COMPILE) $(srcdir)/common/signals-state-save-restore.c
        $(POSTCOMPILE)
 
+new-op.o: ${srcdir}/common/new-op.c
+       $(COMPILE) $(srcdir)/common/new-op.c
+       $(POSTCOMPILE)
+
 #
 # gdb/target/ dependencies
 #
index c494de22dfb89caac14c3f79a8efaf497700f664..6bf7e400adaa7b1bc8988e712435c2cd69458f60 100644 (file)
@@ -21,6 +21,7 @@
 #define COMMON_EXCEPTIONS_H
 
 #include <setjmp.h>
+#include <new>
 
 /* Reasons for calling throw_exceptions().  NOTE: all reason values
    must be less than zero.  enum value 0 is reserved for internal use
@@ -283,6 +284,25 @@ struct gdb_exception_RETURN_MASK_QUIT : public gdb_exception_RETURN_MASK_ALL
 
 #endif /* GDB_XCPT_TRY || GDB_XCPT_RAW_TRY */
 
+/* An exception type that inherits from both std::bad_alloc and a gdb
+   exception.  This is necessary because operator new can only throw
+   std::bad_alloc, and OTOH, we want exceptions thrown due to memory
+   allocation error to be caught by all the CATCH/RETURN_MASK_ALL
+   spread around the codebase.  */
+
+struct gdb_quit_bad_alloc
+  : public gdb_exception_RETURN_MASK_QUIT,
+    public std::bad_alloc
+{
+  explicit gdb_quit_bad_alloc (gdb_exception ex)
+    : std::bad_alloc ()
+  {
+    gdb_exception *self = this;
+
+    *self = ex;
+  }
+};
+
 /* *INDENT-ON* */
 
 /* Throw an exception (as described by "struct gdb_exception").  When
diff --git a/gdb/common/new-op.c b/gdb/common/new-op.c
new file mode 100644 (file)
index 0000000..5ba4d6e
--- /dev/null
@@ -0,0 +1,85 @@
+/* Replace operator new/new[], for GDB, the GNU debugger.
+
+   Copyright (C) 2016 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 "host-defs.h"
+#include <new>
+
+/* Override operator new / operator new[], in order to internal_error
+   on allocation failure and thus query the user for abort/core
+   dump/continue, just like xmalloc does.  We don't do this from a
+   new-handler function instead (std::set_new_handler) because we want
+   to catch allocation errors from within global constructors too.
+
+   Note that C++ implementations could either have their throw
+   versions call the nothrow versions (libstdc++), or the other way
+   around (clang/libc++).  For that reason, we replace both throw and
+   nothrow variants and call malloc directly.  */
+
+void *
+operator new (std::size_t sz)
+{
+  /* malloc (0) is unpredictable; avoid it.  */
+  if (sz == 0)
+    sz = 1;
+
+  void *p = malloc (sz);       /* ARI: malloc */
+  if (p == NULL)
+    {
+      /* If the user decides to continue debugging, throw a
+        gdb_quit_bad_alloc exception instead of a regular QUIT
+        gdb_exception.  The former extends both std::bad_alloc and a
+        QUIT gdb_exception.  This is necessary because operator new
+        can only ever throw std::bad_alloc, or something that extends
+        it.  */
+      TRY
+       {
+         malloc_failure (sz);
+       }
+      CATCH (ex, RETURN_MASK_ALL)
+       {
+         do_cleanups (all_cleanups ());
+
+         throw gdb_quit_bad_alloc (ex);
+       }
+      END_CATCH
+    }
+  return p;
+}
+
+void *
+operator new (std::size_t sz, const std::nothrow_t&)
+{
+  /* malloc (0) is unpredictable; avoid it.  */
+  if (sz == 0)
+    sz = 1;
+  return malloc (sz);          /* ARI: malloc */
+}
+
+void *
+operator new[] (std::size_t sz)
+{
+   return ::operator new (sz);
+}
+
+void*
+operator new[] (std::size_t sz, const std::nothrow_t&)
+{
+  return ::operator new (sz, std::nothrow);
+}
index 4635c69c670b64464803997c45da43e4aa687898..22f38f2dee4b7bc21d80e27bbb0b3a84bf561299 100644 (file)
@@ -1,3 +1,9 @@
+2016-09-23  Pedro Alves  <palves@redhat.com>
+
+       * Makefile.in (SFILES): Add common/new-op.c.
+       (OBS): Add common/new-op.o.
+       (new-op.o): New rule.
+
 2016-09-21  Simon Marchi  <simon.marchi@ericsson.com>
 
        * .gitinore: Ignore more files.
index 309b4967f5c7f1694c75167c858e097c5f06b223..6d5abd3b50e546cb3b31c9c492313da0c9a4be08 100644 (file)
@@ -180,7 +180,8 @@ SFILES=     $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c $(srcdir)/dll.c \
        $(srcdir)/common/btrace-common.c \
        $(srcdir)/common/fileio.c $(srcdir)/nat/linux-namespaces.c \
        $(srcdir)/arch/arm.c $(srcdir)/common/common-regcache.c \
-       $(srcdir)/arch/arm-linux.c $(srcdir)/arch/arm-get-next-pcs.c
+       $(srcdir)/arch/arm-linux.c $(srcdir)/arch/arm-get-next-pcs.c \
+       $(srcdir)/common/new-op.c
 
 DEPFILES = @GDBSERVER_DEPFILES@
 
@@ -195,7 +196,7 @@ OBS = agent.o ax.o inferiors.o regcache.o remote-utils.o server.o signals.o \
       common-utils.o ptid.o buffer.o format.o filestuff.o dll.o notif.o \
       tdesc.o print-utils.o rsp-low.o errors.o common-debug.o cleanups.o \
       common-exceptions.o symbol.o btrace-common.o fileio.o common-regcache.o \
-      signals-state-save-restore.o \
+      signals-state-save-restore.o new-op.o \
       $(XML_BUILTIN) $(DEPFILES) $(LIBOBJS)
 GDBREPLAY_OBS = gdbreplay.o version.o
 GDBSERVER_LIBS = @GDBSERVER_LIBS@
@@ -723,6 +724,9 @@ common-regcache.o: ../common/common-regcache.c
 signals-state-save-restore.o: ../common/signals-state-save-restore.c
        $(COMPILE) $<
        $(POSTCOMPILE)
+new-op.o: ../common/new-op.c
+       $(COMPILE) $<
+       $(POSTCOMPILE)
 
 # Arch object files rules form ../arch