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