xref: /netbsd-src/external/gpl3/gdb.old/dist/gdbsupport/common-exceptions.h (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
17d62b00eSchristos /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
27d62b00eSchristos 
3*6881a400Schristos    Copyright (C) 1986-2023 Free Software Foundation, Inc.
47d62b00eSchristos 
57d62b00eSchristos    This file is part of GDB.
67d62b00eSchristos 
77d62b00eSchristos    This program is free software; you can redistribute it and/or modify
87d62b00eSchristos    it under the terms of the GNU General Public License as published by
97d62b00eSchristos    the Free Software Foundation; either version 3 of the License, or
107d62b00eSchristos    (at your option) any later version.
117d62b00eSchristos 
127d62b00eSchristos    This program is distributed in the hope that it will be useful,
137d62b00eSchristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
147d62b00eSchristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
157d62b00eSchristos    GNU General Public License for more details.
167d62b00eSchristos 
177d62b00eSchristos    You should have received a copy of the GNU General Public License
187d62b00eSchristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
197d62b00eSchristos 
207d62b00eSchristos #ifndef COMMON_COMMON_EXCEPTIONS_H
217d62b00eSchristos #define COMMON_COMMON_EXCEPTIONS_H
227d62b00eSchristos 
237d62b00eSchristos #include <setjmp.h>
247d62b00eSchristos #include <new>
257d62b00eSchristos #include <memory>
267d62b00eSchristos #include <string>
27*6881a400Schristos #include <functional>
287d62b00eSchristos 
297d62b00eSchristos /* Reasons for calling throw_exceptions().  NOTE: all reason values
307d62b00eSchristos    must be different from zero.  enum value 0 is reserved for internal
317d62b00eSchristos    use as the return value from an initial setjmp().  */
327d62b00eSchristos 
337d62b00eSchristos enum return_reason
347d62b00eSchristos   {
357d62b00eSchristos     /* User interrupt.  */
367d62b00eSchristos     RETURN_QUIT = -2,
377d62b00eSchristos     /* Any other error.  */
387d62b00eSchristos     RETURN_ERROR
397d62b00eSchristos   };
407d62b00eSchristos 
417d62b00eSchristos #define RETURN_MASK(reason)	(1 << (int)(-reason))
427d62b00eSchristos 
437d62b00eSchristos typedef enum
447d62b00eSchristos {
457d62b00eSchristos   RETURN_MASK_QUIT = RETURN_MASK (RETURN_QUIT),
467d62b00eSchristos   RETURN_MASK_ERROR = RETURN_MASK (RETURN_ERROR),
477d62b00eSchristos   RETURN_MASK_ALL = (RETURN_MASK_QUIT | RETURN_MASK_ERROR)
487d62b00eSchristos } return_mask;
497d62b00eSchristos 
507d62b00eSchristos /* Describe all exceptions.  */
517d62b00eSchristos 
527d62b00eSchristos enum errors {
537d62b00eSchristos   GDB_NO_ERROR,
547d62b00eSchristos 
557d62b00eSchristos   /* Any generic error, the corresponding text is in
567d62b00eSchristos      exception.message.  */
577d62b00eSchristos   GENERIC_ERROR,
587d62b00eSchristos 
597d62b00eSchristos   /* Something requested was not found.  */
607d62b00eSchristos   NOT_FOUND_ERROR,
617d62b00eSchristos 
627d62b00eSchristos   /* Thread library lacks support necessary for finding thread local
637d62b00eSchristos      storage.  */
647d62b00eSchristos   TLS_NO_LIBRARY_SUPPORT_ERROR,
657d62b00eSchristos 
667d62b00eSchristos   /* Load module not found while attempting to find thread local storage.  */
677d62b00eSchristos   TLS_LOAD_MODULE_NOT_FOUND_ERROR,
687d62b00eSchristos 
697d62b00eSchristos   /* Thread local storage has not been allocated yet.  */
707d62b00eSchristos   TLS_NOT_ALLOCATED_YET_ERROR,
717d62b00eSchristos 
727d62b00eSchristos   /* Something else went wrong while attempting to find thread local
737d62b00eSchristos      storage.  The ``struct gdb_exception'' message field provides
747d62b00eSchristos      more detail.  */
757d62b00eSchristos   TLS_GENERIC_ERROR,
767d62b00eSchristos 
777d62b00eSchristos   /* Problem parsing an XML document.  */
787d62b00eSchristos   XML_PARSE_ERROR,
797d62b00eSchristos 
807d62b00eSchristos   /* Error accessing memory.  */
817d62b00eSchristos   MEMORY_ERROR,
827d62b00eSchristos 
837d62b00eSchristos   /* Value not available.  E.g., a register was not collected in a
847d62b00eSchristos      traceframe.  */
857d62b00eSchristos   NOT_AVAILABLE_ERROR,
867d62b00eSchristos 
877d62b00eSchristos   /* Value was optimized out.  Note: if the value was a register, this
887d62b00eSchristos      means the register was not saved in the frame.  */
897d62b00eSchristos   OPTIMIZED_OUT_ERROR,
907d62b00eSchristos 
917d62b00eSchristos   /* DW_OP_entry_value resolving failed.  */
927d62b00eSchristos   NO_ENTRY_VALUE_ERROR,
937d62b00eSchristos 
947d62b00eSchristos   /* Target throwing an error has been closed.  Current command should be
957d62b00eSchristos      aborted as the inferior state is no longer valid.  */
967d62b00eSchristos   TARGET_CLOSE_ERROR,
977d62b00eSchristos 
987d62b00eSchristos   /* An undefined command was executed.  */
997d62b00eSchristos   UNDEFINED_COMMAND_ERROR,
1007d62b00eSchristos 
1017d62b00eSchristos   /* Requested feature, method, mechanism, etc. is not supported.  */
1027d62b00eSchristos   NOT_SUPPORTED_ERROR,
1037d62b00eSchristos 
1047d62b00eSchristos   /* The number of candidates generated during line completion has
1057d62b00eSchristos      reached the user's specified limit.  This isn't an error, this exception
1067d62b00eSchristos      is used to halt searching for more completions, but for consistency
1077d62b00eSchristos      "_ERROR" is appended to the name.  */
1087d62b00eSchristos   MAX_COMPLETIONS_REACHED_ERROR,
1097d62b00eSchristos 
1107d62b00eSchristos   /* Add more errors here.  */
1117d62b00eSchristos   NR_ERRORS
1127d62b00eSchristos };
1137d62b00eSchristos 
1147d62b00eSchristos struct gdb_exception
1157d62b00eSchristos {
1167d62b00eSchristos   gdb_exception ()
1177d62b00eSchristos     : reason ((enum return_reason) 0),
1187d62b00eSchristos       error (GDB_NO_ERROR)
1197d62b00eSchristos   {
1207d62b00eSchristos   }
1217d62b00eSchristos 
1227d62b00eSchristos   gdb_exception (enum return_reason r, enum errors e)
1237d62b00eSchristos     : reason (r),
1247d62b00eSchristos       error (e)
1257d62b00eSchristos   {
1267d62b00eSchristos   }
1277d62b00eSchristos 
1287d62b00eSchristos   gdb_exception (enum return_reason r, enum errors e,
1297d62b00eSchristos 		 const char *fmt, va_list ap)
1307d62b00eSchristos     ATTRIBUTE_PRINTF (4, 0)
1317d62b00eSchristos     : reason (r),
1327d62b00eSchristos       error (e),
1337d62b00eSchristos       message (std::make_shared<std::string> (string_vprintf (fmt, ap)))
1347d62b00eSchristos   {
1357d62b00eSchristos   }
1367d62b00eSchristos 
1377d62b00eSchristos   /* The move constructor exists so that we can mark it "noexcept",
1387d62b00eSchristos      which is a good practice for any sort of exception object.  */
1397d62b00eSchristos   explicit gdb_exception (gdb_exception &&other) noexcept = default;
1407d62b00eSchristos 
1417d62b00eSchristos   /* The copy constructor exists so that we can mark it "noexcept",
1427d62b00eSchristos      which is a good practice for any sort of exception object.  */
1437d62b00eSchristos   gdb_exception (const gdb_exception &other) noexcept
1447d62b00eSchristos     : reason (other.reason),
1457d62b00eSchristos       error (other.error),
1467d62b00eSchristos       message (other.message)
1477d62b00eSchristos   {
1487d62b00eSchristos   }
1497d62b00eSchristos 
1507d62b00eSchristos   /* The assignment operator exists so that we can mark it "noexcept",
1517d62b00eSchristos      which is a good practice for any sort of exception object.  */
1527d62b00eSchristos   gdb_exception &operator= (const gdb_exception &other) noexcept
1537d62b00eSchristos   {
1547d62b00eSchristos     reason = other.reason;
1557d62b00eSchristos     error = other.error;
1567d62b00eSchristos     message = other.message;
1577d62b00eSchristos     return *this;
1587d62b00eSchristos   }
1597d62b00eSchristos 
1607d62b00eSchristos   gdb_exception &operator= (gdb_exception &&other) noexcept = default;
1617d62b00eSchristos 
1627d62b00eSchristos   /* Return the contents of the exception message, as a C string.  The
1637d62b00eSchristos      string remains owned by the exception object.  */
1647d62b00eSchristos   const char *what () const noexcept
1657d62b00eSchristos   {
1667d62b00eSchristos     return message->c_str ();
1677d62b00eSchristos   }
1687d62b00eSchristos 
169*6881a400Schristos   /* Compare two exceptions.  */
170*6881a400Schristos   bool operator== (const gdb_exception &other) const
171*6881a400Schristos   {
172*6881a400Schristos     const char *msg1 = message == nullptr ? "" : what ();
173*6881a400Schristos     const char *msg2 = other.message == nullptr ? "" : other.what ();
174*6881a400Schristos 
175*6881a400Schristos     return (reason == other.reason
176*6881a400Schristos 	    && error == other.error
177*6881a400Schristos 	    && strcmp (msg1, msg2) == 0);
178*6881a400Schristos   }
179*6881a400Schristos 
180*6881a400Schristos   /* Compare two exceptions.  */
181*6881a400Schristos   bool operator!= (const gdb_exception &other) const
182*6881a400Schristos   {
183*6881a400Schristos     return !(*this == other);
184*6881a400Schristos   }
185*6881a400Schristos 
1867d62b00eSchristos   enum return_reason reason;
1877d62b00eSchristos   enum errors error;
1887d62b00eSchristos   std::shared_ptr<std::string> message;
1897d62b00eSchristos };
1907d62b00eSchristos 
191*6881a400Schristos namespace std
192*6881a400Schristos {
193*6881a400Schristos 
194*6881a400Schristos /* Specialization of std::hash for gdb_exception.  */
195*6881a400Schristos template<>
196*6881a400Schristos struct hash<gdb_exception>
197*6881a400Schristos {
198*6881a400Schristos   size_t operator() (const gdb_exception &exc) const
199*6881a400Schristos   {
200*6881a400Schristos     size_t result = exc.reason + exc.error;
201*6881a400Schristos     if (exc.message != nullptr)
202*6881a400Schristos       result += std::hash<std::string> {} (*exc.message);
203*6881a400Schristos     return result;
204*6881a400Schristos   }
205*6881a400Schristos };
206*6881a400Schristos 
207*6881a400Schristos }
208*6881a400Schristos 
2097d62b00eSchristos /* Functions to drive the sjlj-based exceptions state machine.  Though
2107d62b00eSchristos    declared here by necessity, these functions should be considered
2117d62b00eSchristos    internal to the exceptions subsystem and not used other than via
2127d62b00eSchristos    the TRY/CATCH (or TRY_SJLJ/CATCH_SJLJ) macros defined below.  */
2137d62b00eSchristos 
2147d62b00eSchristos extern jmp_buf *exceptions_state_mc_init (void);
2157d62b00eSchristos extern int exceptions_state_mc_action_iter (void);
2167d62b00eSchristos extern int exceptions_state_mc_action_iter_1 (void);
2177d62b00eSchristos extern int exceptions_state_mc_catch (struct gdb_exception *, int);
2187d62b00eSchristos 
2197d62b00eSchristos /* Macro to wrap up standard try/catch behavior.
2207d62b00eSchristos 
2217d62b00eSchristos    The double loop lets us correctly handle code "break"ing out of the
2227d62b00eSchristos    try catch block.  (It works as the "break" only exits the inner
2237d62b00eSchristos    "while" loop, the outer for loop detects this handling it
2247d62b00eSchristos    correctly.)  Of course "return" and "goto" are not so lucky.
2257d62b00eSchristos 
2267d62b00eSchristos    For instance:
2277d62b00eSchristos 
2287d62b00eSchristos    *INDENT-OFF*
2297d62b00eSchristos 
2307d62b00eSchristos    TRY_SJLJ
2317d62b00eSchristos      {
2327d62b00eSchristos      }
2337d62b00eSchristos    CATCH_SJLJ (e, RETURN_MASK_ERROR)
2347d62b00eSchristos      {
2357d62b00eSchristos        switch (e.reason)
2367d62b00eSchristos 	 {
2377d62b00eSchristos 	   case RETURN_ERROR: ...
2387d62b00eSchristos 	 }
2397d62b00eSchristos      }
2407d62b00eSchristos    END_CATCH_SJLJ
2417d62b00eSchristos 
2427d62b00eSchristos    The SJLJ variants are needed in some cases where gdb exceptions
2437d62b00eSchristos    need to cross third-party library code compiled without exceptions
2447d62b00eSchristos    support (e.g., readline).  */
2457d62b00eSchristos 
2467d62b00eSchristos #define TRY_SJLJ \
2477d62b00eSchristos      { \
2487d62b00eSchristos        jmp_buf *buf = \
2497d62b00eSchristos 	 exceptions_state_mc_init (); \
2507d62b00eSchristos        setjmp (*buf); \
2517d62b00eSchristos      } \
2527d62b00eSchristos      while (exceptions_state_mc_action_iter ()) \
2537d62b00eSchristos        while (exceptions_state_mc_action_iter_1 ())
2547d62b00eSchristos 
2557d62b00eSchristos #define CATCH_SJLJ(EXCEPTION, MASK)				\
2567d62b00eSchristos   {							\
2577d62b00eSchristos     struct gdb_exception EXCEPTION;				\
2587d62b00eSchristos     if (exceptions_state_mc_catch (&(EXCEPTION), MASK))
2597d62b00eSchristos 
2607d62b00eSchristos #define END_CATCH_SJLJ				\
2617d62b00eSchristos   }
2627d62b00eSchristos 
2637d62b00eSchristos /* The exception types client code may catch.  They're just shims
2647d62b00eSchristos    around gdb_exception that add nothing but type info.  Which is used
2657d62b00eSchristos    is selected depending on the MASK argument passed to CATCH.  */
2667d62b00eSchristos 
2677d62b00eSchristos struct gdb_exception_error : public gdb_exception
2687d62b00eSchristos {
2697d62b00eSchristos   gdb_exception_error (enum errors e, const char *fmt, va_list ap)
2707d62b00eSchristos     ATTRIBUTE_PRINTF (3, 0)
2717d62b00eSchristos     : gdb_exception (RETURN_ERROR, e, fmt, ap)
2727d62b00eSchristos   {
2737d62b00eSchristos   }
2747d62b00eSchristos 
2757d62b00eSchristos   explicit gdb_exception_error (gdb_exception &&ex) noexcept
2767d62b00eSchristos     : gdb_exception (std::move (ex))
2777d62b00eSchristos   {
2787d62b00eSchristos     gdb_assert (ex.reason == RETURN_ERROR);
2797d62b00eSchristos   }
2807d62b00eSchristos };
2817d62b00eSchristos 
2827d62b00eSchristos struct gdb_exception_quit : public gdb_exception
2837d62b00eSchristos {
2847d62b00eSchristos   gdb_exception_quit (const char *fmt, va_list ap)
2857d62b00eSchristos     ATTRIBUTE_PRINTF (2, 0)
2867d62b00eSchristos     : gdb_exception (RETURN_QUIT, GDB_NO_ERROR, fmt, ap)
2877d62b00eSchristos   {
2887d62b00eSchristos   }
2897d62b00eSchristos 
2907d62b00eSchristos   explicit gdb_exception_quit (gdb_exception &&ex) noexcept
2917d62b00eSchristos     : gdb_exception (std::move (ex))
2927d62b00eSchristos   {
2937d62b00eSchristos     gdb_assert (ex.reason == RETURN_QUIT);
2947d62b00eSchristos   }
2957d62b00eSchristos };
2967d62b00eSchristos 
2977d62b00eSchristos /* An exception type that inherits from both std::bad_alloc and a gdb
2987d62b00eSchristos    exception.  This is necessary because operator new can only throw
2997d62b00eSchristos    std::bad_alloc, and OTOH, we want exceptions thrown due to memory
3007d62b00eSchristos    allocation error to be caught by all the CATCH/RETURN_MASK_ALL
3017d62b00eSchristos    spread around the codebase.  */
3027d62b00eSchristos 
3037d62b00eSchristos struct gdb_quit_bad_alloc
3047d62b00eSchristos   : public gdb_exception_quit,
3057d62b00eSchristos     public std::bad_alloc
3067d62b00eSchristos {
3077d62b00eSchristos   explicit gdb_quit_bad_alloc (gdb_exception &&ex) noexcept
3087d62b00eSchristos     : gdb_exception_quit (std::move (ex)),
3097d62b00eSchristos       std::bad_alloc ()
3107d62b00eSchristos   {
3117d62b00eSchristos   }
3127d62b00eSchristos };
3137d62b00eSchristos 
3147d62b00eSchristos /* *INDENT-ON* */
3157d62b00eSchristos 
3167d62b00eSchristos /* Throw an exception (as described by "struct gdb_exception"),
3177d62b00eSchristos    landing in the inner most containing exception handler established
3187d62b00eSchristos    using TRY/CATCH.  */
3197d62b00eSchristos extern void throw_exception (gdb_exception &&exception)
3207d62b00eSchristos      ATTRIBUTE_NORETURN;
3217d62b00eSchristos 
3227d62b00eSchristos /* Throw an exception by executing a LONG JUMP to the inner most
3237d62b00eSchristos    containing exception handler established using TRY_SJLJ.  Necessary
3247d62b00eSchristos    in some cases where we need to throw GDB exceptions across
3257d62b00eSchristos    third-party library code (e.g., readline).  */
3267d62b00eSchristos extern void throw_exception_sjlj (const struct gdb_exception &exception)
3277d62b00eSchristos      ATTRIBUTE_NORETURN;
3287d62b00eSchristos 
3297d62b00eSchristos /* Convenience wrappers around throw_exception that throw GDB
3307d62b00eSchristos    errors.  */
3317d62b00eSchristos extern void throw_verror (enum errors, const char *fmt, va_list ap)
3327d62b00eSchristos      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 0);
3337d62b00eSchristos extern void throw_vquit (const char *fmt, va_list ap)
3347d62b00eSchristos      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 0);
3357d62b00eSchristos extern void throw_error (enum errors error, const char *fmt, ...)
3367d62b00eSchristos      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (2, 3);
3377d62b00eSchristos extern void throw_quit (const char *fmt, ...)
3387d62b00eSchristos      ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
3397d62b00eSchristos 
3407d62b00eSchristos #endif /* COMMON_COMMON_EXCEPTIONS_H */
341