GDB (xrefs)
common-exceptions.c
Go to the documentation of this file.
1 /* Exception (throw catch) mechanism, for GDB, the GNU debugger.
2 
3  Copyright (C) 1986-2018 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-defs.h"
21 #include "common-exceptions.h"
22 
23 const struct gdb_exception exception_none = { (enum return_reason) 0, GDB_NO_ERROR, NULL };
24 
25 /* Possible catcher states. */
27  /* Initial state, a new catcher has just been created. */
29  /* The catch code is running. */
32  /* The catch code threw an exception. */
34 };
35 
36 /* Possible catcher actions. */
41 };
42 
43 struct catcher
44 {
46  /* Jump buffer pointing back at the exception handler. */
47  jmp_buf buf;
48  /* Status buffer belonging to the exception handler. */
51  /* Back link. */
52  struct catcher *prev;
53 };
54 
55 /* Where to go for throw_exception(). */
56 static struct catcher *current_catcher;
57 
58 #if GDB_XCPT == GDB_XCPT_SJMP
59 
60 /* Return length of current_catcher list. */
61 
62 static int
63 catcher_list_size (void)
64 {
65  int size;
66  struct catcher *catcher;
67 
68  for (size = 0, catcher = current_catcher;
69  catcher != NULL;
70  catcher = catcher->prev)
71  ++size;
72 
73  return size;
74 }
75 
76 #endif
77 
78 jmp_buf *
80 {
81  struct catcher *new_catcher = XCNEW (struct catcher);
82 
83  /* Start with no exception. */
84  new_catcher->exception = exception_none;
85 
86  /* Prevent error/quit during FUNC from calling cleanups established
87  prior to here. */
88  new_catcher->saved_cleanup_chain = save_cleanups ();
89 
90  /* Push this new catcher on the top. */
91  new_catcher->prev = current_catcher;
92  current_catcher = new_catcher;
93  new_catcher->state = CATCHER_CREATED;
94 
95  return &new_catcher->buf;
96 }
97 
98 static void
100 {
101  struct catcher *old_catcher = current_catcher;
102 
103  current_catcher = old_catcher->prev;
104 
105  /* Restore the cleanup chain, the error/quit messages, and the uiout
106  builder, to their original states. */
107 
108  restore_cleanups (old_catcher->saved_cleanup_chain);
109 
110  xfree (old_catcher);
111 }
112 
113 /* Catcher state machine. Returns non-zero if the m/c should be run
114  again, zero if it should abort. */
115 
116 static int
118 {
119  switch (current_catcher->state)
120  {
121  case CATCHER_CREATED:
122  switch (action)
123  {
124  case CATCH_ITER:
125  /* Allow the code to run the catcher. */
127  return 1;
128  default:
129  internal_error (__FILE__, __LINE__, _("bad state"));
130  }
131  case CATCHER_RUNNING:
132  switch (action)
133  {
134  case CATCH_ITER:
135  /* No error/quit has occured. */
136  return 0;
137  case CATCH_ITER_1:
139  return 1;
140  case CATCH_THROWING:
142  /* See also throw_exception. */
143  return 1;
144  default:
145  internal_error (__FILE__, __LINE__, _("bad switch"));
146  }
147  case CATCHER_RUNNING_1:
148  switch (action)
149  {
150  case CATCH_ITER:
151  /* The did a "break" from the inner while loop. */
152  return 0;
153  case CATCH_ITER_1:
155  return 0;
156  case CATCH_THROWING:
158  /* See also throw_exception. */
159  return 1;
160  default:
161  internal_error (__FILE__, __LINE__, _("bad switch"));
162  }
163  case CATCHER_ABORTING:
164  switch (action)
165  {
166  case CATCH_ITER:
167  {
168  /* Exit normally if this catcher can handle this
169  exception. The caller analyses the func return
170  values. */
171  return 0;
172  }
173  default:
174  internal_error (__FILE__, __LINE__, _("bad state"));
175  }
176  default:
177  internal_error (__FILE__, __LINE__, _("bad switch"));
178  }
179 }
180 
181 int
183  int mask)
184 {
186  catcher_pop ();
187 
188  if (exception->reason < 0)
189  {
190  if (mask & RETURN_MASK (exception->reason))
191  {
192  /* Exit normally and let the caller handle the
193  exception. */
194  return 1;
195  }
196 
197  /* The caller didn't request that the event be caught, relay the
198  event to the next exception_catch/CATCH_SJLJ. */
200  }
201 
202  /* No exception was thrown. */
203  return 0;
204 }
205 
206 int
208 {
210 }
211 
212 int
214 {
216 }
217 
218 #if GDB_XCPT != GDB_XCPT_SJMP
219 
220 /* How many nested TRY blocks we have. See exception_messages and
221  throw_it. */
222 
223 static int try_scope_depth;
224 
225 /* Called on entry to a TRY scope. */
226 
227 void *
229 {
230  ++try_scope_depth;
231  return (void *) save_cleanups ();
232 }
233 
234 /* Called on exit of a TRY scope, either normal exit or exception
235  exit. */
236 
237 void
238 exception_try_scope_exit (void *saved_state)
239 {
240  restore_cleanups ((struct cleanup *) saved_state);
241  --try_scope_depth;
242 }
243 
244 /* Called by the default catch block. IOW, we'll get here before
245  jumping out to the next outermost scope an exception if a GDB
246  exception is not caught. */
247 
248 void
250 {
251  /* Run this scope's cleanups before re-throwing to the next
252  outermost scope. */
254  throw;
255 }
256 
257 /* Copy the 'gdb_exception' portion of FROM to TO. */
258 
259 static void
260 gdb_exception_sliced_copy (struct gdb_exception *to, const struct gdb_exception *from)
261 {
262  *to = *from;
263 }
264 
265 #endif /* !GDB_XCPT_SJMP */
266 
267 /* Return EXCEPTION to the nearest containing CATCH_SJLJ block. */
268 
269 void
271 {
273 
274  /* Jump to the nearest CATCH_SJLJ block, communicating REASON to
275  that call via setjmp's return value. Note that REASON can't be
276  zero, by definition in common-exceptions.h. */
279  longjmp (current_catcher->buf, exception.reason);
280 }
281 
282 #if GDB_XCPT != GDB_XCPT_SJMP
283 
284 /* Implementation of throw_exception that uses C++ try/catch. */
285 
286 static ATTRIBUTE_NORETURN void
288 {
290 
292  {
294 
296  throw ex;
297  }
298  else if (exception.reason == RETURN_ERROR)
299  {
301 
303  throw ex;
304  }
305  else
306  gdb_assert_not_reached ("invalid return reason");
307 }
308 
309 #endif
310 
311 void
313 {
314 #if GDB_XCPT == GDB_XCPT_SJMP
316 #else
318 #endif
319 }
320 
321 /* A stack of exception messages.
322  This is needed to handle nested calls to throw_it: we don't want to
323  xfree space for a message before it's used.
324  This can happen if we throw an exception during a cleanup:
325  An outer TRY_CATCH may have an exception message it wants to print,
326  but while doing cleanups further calls to throw_it are made.
327 
328  This is indexed by the size of the current_catcher list.
329  It is a dynamically allocated array so that we don't care how deeply
330  GDB nests its TRY_CATCHs. */
331 static char **exception_messages;
332 
333 /* The number of currently allocated entries in exception_messages. */
335 
336 static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0)
337 throw_it (enum return_reason reason, enum errors error, const char *fmt,
338  va_list ap)
339 {
340  struct gdb_exception e;
341  char *new_message;
342 #if GDB_XCPT == GDB_XCPT_SJMP
343  int depth = catcher_list_size ();
344 #else
345  int depth = try_scope_depth;
346 #endif
347 
348  gdb_assert (depth > 0);
349 
350  /* Note: The new message may use an old message's text. */
351  new_message = xstrvprintf (fmt, ap);
352 
353  if (depth > exception_messages_size)
354  {
355  int old_size = exception_messages_size;
356 
357  exception_messages_size = depth + 10;
360  memset (exception_messages + old_size, 0,
361  (exception_messages_size - old_size) * sizeof (char *));
362  }
363 
364  xfree (exception_messages[depth - 1]);
365  exception_messages[depth - 1] = new_message;
366 
367  /* Create the exception. */
368  e.reason = reason;
369  e.error = error;
370  e.message = new_message;
371 
372  /* Throw the exception. */
373  throw_exception (e);
374 }
375 
376 void
377 throw_verror (enum errors error, const char *fmt, va_list ap)
378 {
379  throw_it (RETURN_ERROR, error, fmt, ap);
380 }
381 
382 void
383 throw_vquit (const char *fmt, va_list ap)
384 {
385  throw_it (RETURN_QUIT, GDB_NO_ERROR, fmt, ap);
386 }
387 
388 void
389 throw_error (enum errors error, const char *fmt, ...)
390 {
391  va_list args;
392 
393  va_start (args, fmt);
394  throw_verror (error, fmt, args);
395  va_end (args);
396 }
397 
398 void
399 throw_quit (const char *fmt, ...)
400 {
401  va_list args;
402 
403  va_start (args, fmt);
404  throw_vquit (fmt, args);
405  va_end (args);
406 }
void xfree(void *)
return_reason
static void gdb_exception_sliced_copy(struct gdb_exception *to, const struct gdb_exception *from)
char * xstrvprintf(const char *format, va_list ap)
Definition: common-utils.c:119
#define RETURN_MASK(reason)
int exceptions_state_mc_action_iter(void)
jmp_buf * exceptions_state_mc_init(void)
enum errors error
void * memset(T *s, int c, size_t n)=delete
void internal_error(const char *file, int line, const char *fmt,...)
Definition: errors.c:50
const struct gdb_exception exception_none
void restore_cleanups(struct cleanup *chain)
Definition: cleanups.c:273
static int exceptions_state_mc(enum catcher_action action)
#define _(String)
Definition: gdb_locale.h:35
struct gdb_exception exception
struct cleanup * all_cleanups(void)
Definition: cleanups.c:165
struct cleanup * save_cleanups(void)
Definition: cleanups.c:242
static void catcher_pop(void)
void throw_exception_sjlj(struct gdb_exception exception)
#define gdb_assert_not_reached(message)
Definition: gdb_assert.h:55
void exception_try_scope_exit(void *saved_state)
static int try_scope_depth
void exception_rethrow(void)
int exceptions_state_mc_action_iter_1(void)
static struct catcher * current_catcher
static int exception_messages_size
int exceptions_state_mc_catch(struct gdb_exception *exception, int mask)
#define XRESIZEVEC(T, P, N)
Definition: poison.h:169
#define gdb_assert(expr)
Definition: gdb_assert.h:32
void throw_exception(struct gdb_exception exception)
static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF(3, 0)
#define XCNEW(T)
Definition: poison.h:121
void throw_verror(enum errors error, const char *fmt, va_list ap)
void throw_vquit(const char *fmt, va_list ap)
struct cleanup * saved_cleanup_chain
static char ** exception_messages
catcher_action
void throw_quit(const char *fmt,...)
static ATTRIBUTE_NORETURN void throw_exception_cxx(struct gdb_exception exception)
void * exception_try_scope_entry(void)
const char * message
struct catcher * prev
enum catcher_state state
catcher_state
void error(const char *fmt,...)
Definition: errors.c:38
size_t size
Definition: go32-nat.c:242
jmp_buf buf
void throw_error(enum errors error, const char *fmt,...)
void do_cleanups(struct cleanup *old_chain)
Definition: cleanups.c:174
enum return_reason reason