common-exceptions.cc 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
  2. Copyright (C) 1986-2022 Free Software Foundation, Inc.
  3. This file is part of GDB.
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>. */
  14. #include "common-defs.h"
  15. #include "common-exceptions.h"
  16. #include <forward_list>
  17. /* Possible catcher states. */
  18. enum catcher_state {
  19. /* Initial state, a new catcher has just been created. */
  20. CATCHER_CREATED,
  21. /* The catch code is running. */
  22. CATCHER_RUNNING,
  23. CATCHER_RUNNING_1,
  24. /* The catch code threw an exception. */
  25. CATCHER_ABORTING
  26. };
  27. /* Possible catcher actions. */
  28. enum catcher_action {
  29. CATCH_ITER,
  30. CATCH_ITER_1,
  31. CATCH_THROWING
  32. };
  33. struct catcher
  34. {
  35. enum catcher_state state = CATCHER_CREATED;
  36. /* Jump buffer pointing back at the exception handler. */
  37. jmp_buf buf;
  38. /* Status buffer belonging to the exception handler. */
  39. struct gdb_exception exception;
  40. };
  41. /* Where to go for throw_exception(). */
  42. static std::forward_list<struct catcher> catchers;
  43. jmp_buf *
  44. exceptions_state_mc_init ()
  45. {
  46. catchers.emplace_front ();
  47. return &catchers.front ().buf;
  48. }
  49. /* Catcher state machine. Returns non-zero if the m/c should be run
  50. again, zero if it should abort. */
  51. static int
  52. exceptions_state_mc (enum catcher_action action)
  53. {
  54. switch (catchers.front ().state)
  55. {
  56. case CATCHER_CREATED:
  57. switch (action)
  58. {
  59. case CATCH_ITER:
  60. /* Allow the code to run the catcher. */
  61. catchers.front ().state = CATCHER_RUNNING;
  62. return 1;
  63. default:
  64. internal_error (__FILE__, __LINE__, _("bad state"));
  65. }
  66. case CATCHER_RUNNING:
  67. switch (action)
  68. {
  69. case CATCH_ITER:
  70. /* No error/quit has occured. */
  71. return 0;
  72. case CATCH_ITER_1:
  73. catchers.front ().state = CATCHER_RUNNING_1;
  74. return 1;
  75. case CATCH_THROWING:
  76. catchers.front ().state = CATCHER_ABORTING;
  77. /* See also throw_exception. */
  78. return 1;
  79. default:
  80. internal_error (__FILE__, __LINE__, _("bad switch"));
  81. }
  82. case CATCHER_RUNNING_1:
  83. switch (action)
  84. {
  85. case CATCH_ITER:
  86. /* The did a "break" from the inner while loop. */
  87. return 0;
  88. case CATCH_ITER_1:
  89. catchers.front ().state = CATCHER_RUNNING;
  90. return 0;
  91. case CATCH_THROWING:
  92. catchers.front ().state = CATCHER_ABORTING;
  93. /* See also throw_exception. */
  94. return 1;
  95. default:
  96. internal_error (__FILE__, __LINE__, _("bad switch"));
  97. }
  98. case CATCHER_ABORTING:
  99. switch (action)
  100. {
  101. case CATCH_ITER:
  102. {
  103. /* Exit normally if this catcher can handle this
  104. exception. The caller analyses the func return
  105. values. */
  106. return 0;
  107. }
  108. default:
  109. internal_error (__FILE__, __LINE__, _("bad state"));
  110. }
  111. default:
  112. internal_error (__FILE__, __LINE__, _("bad switch"));
  113. }
  114. }
  115. int
  116. exceptions_state_mc_catch (struct gdb_exception *exception,
  117. int mask)
  118. {
  119. *exception = std::move (catchers.front ().exception);
  120. catchers.pop_front ();
  121. if (exception->reason < 0)
  122. {
  123. if (mask & RETURN_MASK (exception->reason))
  124. {
  125. /* Exit normally and let the caller handle the
  126. exception. */
  127. return 1;
  128. }
  129. /* The caller didn't request that the event be caught, relay the
  130. event to the next exception_catch/CATCH_SJLJ. */
  131. throw_exception_sjlj (*exception);
  132. }
  133. /* No exception was thrown. */
  134. return 0;
  135. }
  136. int
  137. exceptions_state_mc_action_iter (void)
  138. {
  139. return exceptions_state_mc (CATCH_ITER);
  140. }
  141. int
  142. exceptions_state_mc_action_iter_1 (void)
  143. {
  144. return exceptions_state_mc (CATCH_ITER_1);
  145. }
  146. /* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */
  147. void
  148. throw_exception_sjlj (const struct gdb_exception &exception)
  149. {
  150. /* Jump to the nearest CATCH_SJLJ block, communicating REASON to
  151. that call via setjmp's return value. Note that REASON can't be
  152. zero, by definition in common-exceptions.h. */
  153. exceptions_state_mc (CATCH_THROWING);
  154. enum return_reason reason = exception.reason;
  155. catchers.front ().exception = exception;
  156. longjmp (catchers.front ().buf, reason);
  157. }
  158. /* Implementation of throw_exception that uses C++ try/catch. */
  159. void
  160. throw_exception (gdb_exception &&exception)
  161. {
  162. if (exception.reason == RETURN_QUIT)
  163. throw gdb_exception_quit (std::move (exception));
  164. else if (exception.reason == RETURN_ERROR)
  165. throw gdb_exception_error (std::move (exception));
  166. else
  167. gdb_assert_not_reached ("invalid return reason");
  168. }
  169. static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
  170. throw_it (enum return_reason reason, enum errors error, const char *fmt,
  171. va_list ap)
  172. {
  173. if (reason == RETURN_QUIT)
  174. throw gdb_exception_quit (fmt, ap);
  175. else if (reason == RETURN_ERROR)
  176. throw gdb_exception_error (error, fmt, ap);
  177. else
  178. gdb_assert_not_reached ("invalid return reason");
  179. }
  180. void
  181. throw_verror (enum errors error, const char *fmt, va_list ap)
  182. {
  183. throw_it (RETURN_ERROR, error, fmt, ap);
  184. }
  185. void
  186. throw_vquit (const char *fmt, va_list ap)
  187. {
  188. throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
  189. }
  190. void
  191. throw_error (enum errors error, const char *fmt, ...)
  192. {
  193. va_list args;
  194. va_start (args, fmt);
  195. throw_verror (error, fmt, args);
  196. va_end (args);
  197. }
  198. void
  199. throw_quit (const char *fmt, ...)
  200. {
  201. va_list args;
  202. va_start (args, fmt);
  203. throw_vquit (fmt, args);
  204. va_end (args);
  205. }