Make SJLJ exceptions more efficient
[deliverable/binutils-gdb.git] / gdb / common / common-exceptions.h
index 4f60ad817cb8515409a3f960d6660e01dfcd053f..d7b25502262e671e1c965699f4072d2a330b6062 100644 (file)
@@ -1,6 +1,6 @@
 /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
 
-   Copyright (C) 1986-2015 Free Software Foundation, Inc.
+   Copyright (C) 1986-2019 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
    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 COMMON_EXCEPTIONS_H
-#define COMMON_EXCEPTIONS_H
+#ifndef COMMON_COMMON_EXCEPTIONS_H
+#define COMMON_COMMON_EXCEPTIONS_H
 
-#include "gdb_setjmp.h"
+#include <setjmp.h>
+#include <new>
+#include <memory>
+#include <string>
 
 /* Reasons for calling throw_exceptions().  NOTE: all reason values
-   must be less than zero.  enum value 0 is reserved for internal use
-   as the return value from an initial setjmp().  The function
-   catch_exceptions() reserves values >= 0 as legal results from its
-   wrapped function.  */
+   must be different from zero.  enum value 0 is reserved for internal
+   use as the return value from an initial setjmp().  */
 
 enum return_reason
   {
@@ -86,7 +87,7 @@ enum errors {
      means the register was not saved in the frame.  */
   OPTIMIZED_OUT_ERROR,
 
-  /* DW_OP_GNU_entry_value resolving failed.  */
+  /* DW_OP_entry_value resolving failed.  */
   NO_ENTRY_VALUE_ERROR,
 
   /* Target throwing an error has been closed.  Current command should be
@@ -99,27 +100,81 @@ enum errors {
   /* Requested feature, method, mechanism, etc. is not supported.  */
   NOT_SUPPORTED_ERROR,
 
+  /* The number of candidates generated during line completion has
+     reached the user's specified limit.  This isn't an error, this exception
+     is used to halt searching for more completions, but for consistency
+     "_ERROR" is appended to the name.  */
+  MAX_COMPLETIONS_REACHED_ERROR,
+
   /* Add more errors here.  */
   NR_ERRORS
 };
 
 struct gdb_exception
 {
+  gdb_exception ()
+    : reason ((enum return_reason) 0),
+      error (GDB_NO_ERROR)
+  {
+  }
+
+  gdb_exception (enum return_reason r, enum errors e)
+    : reason (r),
+      error (e)
+  {
+  }
+
+  gdb_exception (enum return_reason r, enum errors e,
+                const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (4, 0)
+    : reason (r),
+      error (e),
+      message (std::make_shared<std::string> (string_vprintf (fmt, ap)))
+  {
+  }
+
+  /* The copy constructor exists so that we can mark it "noexcept",
+     which is a good practice for any sort of exception object.  */
+  gdb_exception (const gdb_exception &other) noexcept
+    : reason (other.reason),
+      error (other.error),
+      message (other.message)
+  {
+  }
+
+  /* The assignment operator exists so that we can mark it "noexcept",
+     which is a good practice for any sort of exception object.  */
+  gdb_exception &operator= (const gdb_exception &other) noexcept
+  {
+    reason = other.reason;
+    error = other.error;
+    message = other.message;
+    return *this;
+  }
+
+  gdb_exception &operator= (gdb_exception &&other) noexcept = default;
+
+  /* Return the contents of the exception message, as a C string.  The
+     string remains owned by the exception object.  */
+  const char *what () const noexcept
+  {
+    return message->c_str ();
+  }
+
   enum return_reason reason;
   enum errors error;
-  const char *message;
+  std::shared_ptr<std::string> message;
 };
 
-/* Functions to drive the exceptions state machine.  Though declared
-   here by necessity, these functions should be considered internal to
-   the exceptions subsystem and not used other than via the TRY_CATCH
-   macro defined below.  */
+/* Functions to drive the sjlj-based exceptions state machine.  Though
+   declared here by necessity, these functions should be considered
+   internal to the exceptions subsystem and not used other than via
+   the TRY/CATCH (or TRY_SJLJ/CATCH_SJLJ) macros defined below.  */
 
-extern SIGJMP_BUF *exceptions_state_mc_init (volatile struct
-                                            gdb_exception *exception,
-                                            return_mask mask);
+extern jmp_buf *exceptions_state_mc_init (void);
 extern int exceptions_state_mc_action_iter (void);
 extern int exceptions_state_mc_action_iter_1 (void);
+extern int exceptions_state_mc_catch (struct gdb_exception *, int);
 
 /* Macro to wrap up standard try/catch behavior.
 
@@ -132,47 +187,107 @@ extern int exceptions_state_mc_action_iter_1 (void);
 
    *INDENT-OFF*
 
-   volatile struct gdb_exception e;
-   TRY_CATCH (e, RETURN_MASK_ERROR)
+   TRY_SJLJ
      {
      }
-   switch (e.reason)
+   CATCH_SJLJ (e, RETURN_MASK_ERROR)
      {
-     case RETURN_ERROR: ...
+       switch (e.reason)
+         {
+           case RETURN_ERROR: ...
+         }
      }
+   END_CATCH_SJLJ
 
-  */
+   The SJLJ variants are needed in some cases where gdb exceptions
+   need to cross third-party library code compiled without exceptions
+   support (e.g., readline).  */
 
-#define TRY_CATCH(EXCEPTION,MASK) \
+#define TRY_SJLJ \
      { \
-       SIGJMP_BUF *buf = \
-        exceptions_state_mc_init (&(EXCEPTION), (MASK)); \
-       SIGSETJMP (*buf); \
+       jmp_buf *buf = \
+        exceptions_state_mc_init (); \
+       setjmp (*buf); \
      } \
      while (exceptions_state_mc_action_iter ()) \
        while (exceptions_state_mc_action_iter_1 ())
 
-/* *INDENT-ON* */
+#define CATCH_SJLJ(EXCEPTION, MASK)                            \
+  {                                                    \
+    struct gdb_exception EXCEPTION;                            \
+    if (exceptions_state_mc_catch (&(EXCEPTION), MASK))
 
-/* Hook to allow client-specific actions to be performed prior to
-   throwing an exception.  This function must be provided by the
-   client, and will be called before any cleanups are run.  */
+#define END_CATCH_SJLJ                         \
+  }
 
-extern void prepare_to_throw_exception (void);
+/* The exception types client code may catch.  They're just shims
+   around gdb_exception that add nothing but type info.  Which is used
+   is selected depending on the MASK argument passed to CATCH.  */
 
-/* Throw an exception (as described by "struct gdb_exception").  Will
-   execute a LONG JUMP to the inner most containing exception handler
-   established using catch_exceptions() (or similar).
+struct gdb_exception_error : public gdb_exception
+{
+  gdb_exception_error (enum errors e, const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (3, 0)
+    : gdb_exception (RETURN_ERROR, e, fmt, ap)
+  {
+  }
 
-   Code normally throws an exception using error() et.al.  For various
-   reaons, GDB also contains code that throws an exception directly.
-   For instance, the remote*.c targets contain CNTRL-C signal handlers
-   that propogate the QUIT event up the exception chain.  ``This could
-   be a good thing or a dangerous thing.'' -- the Existential
-   Wombat.  */
+  explicit gdb_exception_error (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_ERROR);
+  }
+};
 
-extern void throw_exception (struct gdb_exception exception)
+struct gdb_exception_quit : public gdb_exception
+{
+  gdb_exception_quit (const char *fmt, va_list ap)
+    ATTRIBUTE_PRINTF (2, 0)
+    : gdb_exception (RETURN_QUIT, GDB_NO_ERROR, fmt, ap)
+  {
+  }
+
+  explicit gdb_exception_quit (const gdb_exception &ex) noexcept
+    : gdb_exception (ex)
+  {
+    gdb_assert (ex.reason == RETURN_QUIT);
+  }
+};
+
+/* 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_quit,
+    public std::bad_alloc
+{
+  explicit gdb_quit_bad_alloc (const gdb_exception &ex) noexcept
+    : gdb_exception_quit (ex),
+      std::bad_alloc ()
+  {
+  }
+};
+
+/* *INDENT-ON* */
+
+/* Throw an exception (as described by "struct gdb_exception"),
+   landing in the inner most containing exception handler established
+   using TRY/CATCH.  */
+extern void throw_exception (const gdb_exception &exception)
+     ATTRIBUTE_NORETURN;
+
+/* Throw an exception by executing a LONG JUMP to the inner most
+   containing exception handler established using TRY_SJLJ.  Necessary
+   in some cases where we need to throw GDB exceptions across
+   third-party library code (e.g., readline).  */
+extern void throw_exception_sjlj (const struct gdb_exception &exception)
      ATTRIBUTE_NORETURN;
+
+/* Convenience wrappers around throw_exception that throw GDB
+   errors.  */
 extern void throw_verror (enum errors, const char *fmt, va_list ap)
      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0);
 extern void throw_vquit (const char *fmt, va_list ap)
@@ -182,4 +297,4 @@ extern void throw_error (enum errors error, const char *fmt, ...)
 extern void throw_quit (const char *fmt, ...)
      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
-#endif /* COMMON_EXCEPTIONS_H */
+#endif /* COMMON_COMMON_EXCEPTIONS_H */
This page took 0.028224 seconds and 4 git commands to generate.