Index: gdb/exceptions.c |
diff --git a/gdb/exceptions.c b/gdb/exceptions.c |
index 2ac1283b2f1bdbcd80eb881297a8e6ca96dd69a2..7db9df9eb805662f3c342bac53d0de56f5984f3a 100644 |
--- a/gdb/exceptions.c |
+++ b/gdb/exceptions.c |
@@ -66,6 +66,22 @@ struct catcher |
/* Where to go for throw_exception(). */ |
static struct catcher *current_catcher; |
+/* Return length of current_catcher list. */ |
+ |
+static int |
+catcher_list_size (void) |
+{ |
+ int size; |
+ struct catcher *catcher; |
+ |
+ for (size = 0, catcher = current_catcher; |
+ catcher != NULL; |
+ catcher = catcher->prev) |
+ ++size; |
+ |
+ return size; |
+} |
+ |
EXCEPTIONS_SIGJMP_BUF * |
exceptions_state_mc_init (volatile struct gdb_exception *exception, |
return_mask mask) |
@@ -208,7 +224,7 @@ throw_exception (struct gdb_exception exception) |
quit_flag = 0; |
immediate_quit = 0; |
- do_cleanups (ALL_CLEANUPS); |
+ do_cleanups (all_cleanups ()); |
/* Jump to the containing catch_errors() call, communicating REASON |
to that call via setjmp's return value. Note that REASON can't |
@@ -218,8 +234,6 @@ throw_exception (struct gdb_exception exception) |
EXCEPTIONS_SIGLONGJMP (current_catcher->buf, exception.reason); |
} |
-static char *last_message; |
- |
void |
deprecated_throw_reason (enum return_reason reason) |
{ |
@@ -357,23 +371,53 @@ print_any_exception (struct ui_file *file, const char *prefix, |
} |
} |
+/* A stack of exception messages. |
+ This is needed to handle nested calls to throw_it: we don't want to |
+ xfree space for a message before it's used. |
+ This can happen if we throw an exception during a cleanup: |
+ An outer TRY_CATCH may have an exception message it wants to print, |
+ but while doing cleanups further calls to throw_it are made. |
+ |
+ This is indexed by the size of the current_catcher list. |
+ It is a dynamically allocated array so that we don't care how deeply |
+ GDB nests its TRY_CATCHs. */ |
+static char **exception_messages; |
+ |
+/* The number of currently allocated entries in exception_messages. */ |
+static int exception_messages_size; |
+ |
static void ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (3, 0) |
throw_it (enum return_reason reason, enum errors error, const char *fmt, |
va_list ap) |
{ |
struct gdb_exception e; |
char *new_message; |
+ int depth = catcher_list_size (); |
+ |
+ gdb_assert (depth > 0); |
- /* Save the message. Create the new message before deleting the |
- old, the new message may include the old message text. */ |
+ /* Note: The new message may use an old message's text. */ |
new_message = xstrvprintf (fmt, ap); |
- xfree (last_message); |
- last_message = new_message; |
+ |
+ if (depth > exception_messages_size) |
+ { |
+ int old_size = exception_messages_size; |
+ |
+ exception_messages_size = depth + 10; |
+ exception_messages = (char **) xrealloc (exception_messages, |
+ exception_messages_size |
+ * sizeof (char *)); |
+ memset (exception_messages + old_size, 0, |
+ (exception_messages_size - old_size) * sizeof (char *)); |
+ } |
+ |
+ xfree (exception_messages[depth - 1]); |
+ exception_messages[depth - 1] = new_message; |
/* Create the exception. */ |
e.reason = reason; |
e.error = error; |
- e.message = last_message; |
+ e.message = new_message; |
/* Throw the exception. */ |
throw_exception (e); |