Add support for exception handling with multiple versions of
authorJoel Brobecker <brobecker@gnat.com>
Thu, 29 Mar 2007 21:57:02 +0000 (21:57 +0000)
committerJoel Brobecker <brobecker@gnat.com>
Thu, 29 Mar 2007 21:57:02 +0000 (21:57 +0000)
        the Ada runtime:
        * ada-lang.c: Update general comments on how Ada exception catchpoints
        are implemented.
        (raise_sym_name, raise_unhandled_sym_name, raise_assert_sym_name)
        (__gnat_raise_nodefer_with_msg): Delete.
        (ada_unhandled_exception_name_addr_ftype): New type.
        (exception_support_info): New type.
        (ada_unhandled_exception_name_addr): Add forward declaration.
        (ada_unhandled_exception_name_addr_from_raise): Likewise.
        (default_exception_support_info): New constant.
        (exception_support_info_fallback): Likewise.
        (exception_info): New global variable.
        (ada_exception_support_info_sniffer): New function.
        (ada_executable_changed_observer): Likewise.
        (ada_unhandled_exception_name_addr_from_raise): Renamed from
        ada_unhandled_exception_name_addr.
        (ada_unhandled_exception_name_addr): Reimplement to match the
        latest Ada runtime implementation.
        (error_breakpoint_runtime_sym_not_found): Delete.
        (ada_exception_sym_name): Get the exception sym name from
        exception_info rather than hardcoding it.
        (ada_exception_sal): Add call to ada_exception_support_info_sniffer.
        Update error handling.
        * Makefile.in (ada-lang.o): Add dependency on observer.h.

gdb/ChangeLog
gdb/ada-lang.c

index c534f3b..dfe9ac6 100644 (file)
@@ -1,3 +1,31 @@
+2007-03-29  Joel Brobecker  <brobecker@adacore.com>
+
+       Add support for exception handling with multiple versions of
+       the Ada runtime:
+       * ada-lang.c: Update general comments on how Ada exception catchpoints
+       are implemented.
+       (raise_sym_name, raise_unhandled_sym_name, raise_assert_sym_name)
+       (__gnat_raise_nodefer_with_msg): Delete.
+       (ada_unhandled_exception_name_addr_ftype): New type.
+       (exception_support_info): New type.
+       (ada_unhandled_exception_name_addr): Add forward declaration.
+       (ada_unhandled_exception_name_addr_from_raise): Likewise.
+       (default_exception_support_info): New constant.
+       (exception_support_info_fallback): Likewise.
+       (exception_info): New global variable.
+       (ada_exception_support_info_sniffer): New function.
+       (ada_executable_changed_observer): Likewise.
+       (ada_unhandled_exception_name_addr_from_raise): Renamed from
+       ada_unhandled_exception_name_addr.
+       (ada_unhandled_exception_name_addr): Reimplement to match the
+       latest Ada runtime implementation.
+       (error_breakpoint_runtime_sym_not_found): Delete.
+       (ada_exception_sym_name): Get the exception sym name from
+       exception_info rather than hardcoding it.
+       (ada_exception_sal): Add call to ada_exception_support_info_sniffer.
+       Update error handling.
+       * Makefile.in (ada-lang.o): Add dependency on observer.h.
+
 2007-03-29  Daniel Jacobowitz  <dan@codesourcery.com>
 
        * Makefile.in (coff_solib_h, coff-solib.o, i386v-nat.o, lynx-nat.o)
index 5447e43..134768f 100644 (file)
@@ -56,6 +56,7 @@ Boston, MA 02110-1301, USA.  */
 #include "annotate.h"
 #include "valprint.h"
 #include "source.h"
+#include "observer.h"
 
 #ifndef ADA_RETAIN_DOTS
 #define ADA_RETAIN_DOTS 0
@@ -290,23 +291,6 @@ static char *ada_completer_word_break_characters =
 static const char ADA_MAIN_PROGRAM_SYMBOL_NAME[]
   = "__gnat_ada_main_program_name";
 
-/* The name of the runtime function called when an exception is raised.  */
-static const char raise_sym_name[] = "__gnat_raise_nodefer_with_msg";
-
-/* The name of the runtime function called when an unhandled exception
-   is raised.  */
-static const char raise_unhandled_sym_name[] = "__gnat_unhandled_exception";
-
-/* The name of the runtime function called when an assert failure is
-   raised.  */
-static const char raise_assert_sym_name[] =
-  "system__assertions__raise_assert_failure";
-
-/* A string that reflects the longest exception expression rewrite,
-   aside from the exception name.  */
-static const char longest_exception_template[] =
-  "'__gnat_raise_nodefer_with_msg' if long_integer(e) = long_integer(&)";
-
 /* Limit on the number of warnings to raise per expression evaluation.  */
 static int warning_limit = 2;
 
@@ -8967,6 +8951,12 @@ ada_modulus (struct type * type)
    breakpoint structure of the BP_BREAKPOINT type, but with its own set
    of breakpoint_ops.
 
+   Support in the runtime for exception catchpoints have been changed
+   a few times already, and these changes affect the implementation
+   of these catchpoints.  In order to be able to support several
+   variants of the runtime, we use a sniffer that will determine
+   the runtime variant used by the program being debugged.
+
    At this time, we do not support the use of conditions on Ada exception
    catchpoints.  The COND and COND_STRING fields are therefore set
    to NULL (most of the time, see below).
@@ -8989,6 +8979,136 @@ enum exception_catchpoint_kind
   ex_catch_assert
 };
 
+typedef CORE_ADDR (ada_unhandled_exception_name_addr_ftype) (void);
+
+/* A structure that describes how to support exception catchpoints
+   for a given executable.  */
+
+struct exception_support_info
+{
+   /* The name of the symbol to break on in order to insert
+      a catchpoint on exceptions.  */
+   const char *catch_exception_sym;
+
+   /* The name of the symbol to break on in order to insert
+      a catchpoint on unhandled exceptions.  */
+   const char *catch_exception_unhandled_sym;
+
+   /* The name of the symbol to break on in order to insert
+      a catchpoint on failed assertions.  */
+   const char *catch_assert_sym;
+
+   /* Assuming that the inferior just triggered an unhandled exception
+      catchpoint, this function is responsible for returning the address
+      in inferior memory where the name of that exception is stored.
+      Return zero if the address could not be computed.  */
+   ada_unhandled_exception_name_addr_ftype *unhandled_exception_name_addr;
+};
+
+static CORE_ADDR ada_unhandled_exception_name_addr (void);
+static CORE_ADDR ada_unhandled_exception_name_addr_from_raise (void);
+
+/* The following exception support info structure describes how to
+   implement exception catchpoints with the latest version of the
+   Ada runtime (as of 2007-03-06).  */
+
+static const struct exception_support_info default_exception_support_info =
+{
+  "__gnat_debug_raise_exception", /* catch_exception_sym */
+  "__gnat_unhandled_exception", /* catch_exception_unhandled_sym */
+  "__gnat_debug_raise_assert_failure", /* catch_assert_sym */
+  ada_unhandled_exception_name_addr
+};
+
+/* The following exception support info structure describes how to
+   implement exception catchpoints with a slightly older version
+   of the Ada runtime.  */
+
+static const struct exception_support_info exception_support_info_fallback =
+{
+  "__gnat_raise_nodefer_with_msg", /* catch_exception_sym */
+  "__gnat_unhandled_exception", /* catch_exception_unhandled_sym */
+  "system__assertions__raise_assert_failure",  /* catch_assert_sym */
+  ada_unhandled_exception_name_addr_from_raise
+};
+
+/* For each executable, we sniff which exception info structure to use
+   and cache it in the following global variable.  */
+
+static const struct exception_support_info *exception_info = NULL;
+
+/* Inspect the Ada runtime and determine which exception info structure
+   should be used to provide support for exception catchpoints.
+
+   This function will always set exception_info, or raise an error.  */
+
+static void
+ada_exception_support_info_sniffer (void)
+{
+  struct symbol *sym;
+
+  /* If the exception info is already known, then no need to recompute it.  */
+  if (exception_info != NULL)
+    return;
+
+  /* Check the latest (default) exception support info.  */
+  sym = standard_lookup (default_exception_support_info.catch_exception_sym,
+                         NULL, VAR_DOMAIN);
+  if (sym != NULL)
+    {
+      exception_info = &default_exception_support_info;
+      return;
+    }
+
+  /* Try our fallback exception suport info.  */
+  sym = standard_lookup (exception_support_info_fallback.catch_exception_sym,
+                         NULL, VAR_DOMAIN);
+  if (sym != NULL)
+    {
+      exception_info = &exception_support_info_fallback;
+      return;
+    }
+
+  /* Sometimes, it is normal for us to not be able to find the routine
+     we are looking for.  This happens when the program is linked with
+     the shared version of the GNAT runtime, and the program has not been
+     started yet.  Inform the user of these two possible causes if
+     applicable.  */
+
+  if (ada_update_initial_language (language_unknown, NULL) != language_ada)
+    error (_("Unable to insert catchpoint.  Is this an Ada main program?"));
+
+  /* If the symbol does not exist, then check that the program is
+     already started, to make sure that shared libraries have been
+     loaded.  If it is not started, this may mean that the symbol is
+     in a shared library.  */
+
+  if (ptid_get_pid (inferior_ptid) == 0)
+    error (_("Unable to insert catchpoint. Try to start the program first."));
+
+  /* At this point, we know that we are debugging an Ada program and
+     that the inferior has been started, but we still are not able to
+     find the run-time symbols. That can mean that we are in
+     configurable run time mode, or that a-except as been optimized
+     out by the linker...  In any case, at this point it is not worth
+     supporting this feature.  */
+
+  error (_("Cannot insert catchpoints in this configuration."));
+}
+
+/* An observer of "executable_changed" events.
+   Its role is to clear certain cached values that need to be recomputed
+   each time a new executable is loaded by GDB.  */
+
+static void
+ada_executable_changed_observer (void *unused)
+{
+  /* If the executable changed, then it is possible that the Ada runtime
+     is different.  So we need to invalidate the exception support info
+     cache.  */
+  exception_info = NULL;
+}
+
 /* Return the name of the function at PC, NULL if could not find it.
    This function only checks the debugging information, not the symbol
    table.  */
@@ -9089,6 +9209,17 @@ ada_find_printable_frame (struct frame_info *fi)
 static CORE_ADDR
 ada_unhandled_exception_name_addr (void)
 {
+  return parse_and_eval_address ("e.full_name");
+}
+
+/* Same as ada_unhandled_exception_name_addr, except that this function
+   should be used when the inferior uses an older version of the runtime,
+   where the exception name needs to be extracted from a specific frame
+   several frames up in the callstack.  */
+
+static CORE_ADDR
+ada_unhandled_exception_name_addr_from_raise (void)
+{
   int frame_level;
   struct frame_info *fi;
 
@@ -9106,7 +9237,7 @@ ada_unhandled_exception_name_addr (void)
       const char *func_name =
         function_name_from_pc (get_frame_address_in_block (fi));
       if (func_name != NULL
-          && strcmp (func_name, raise_sym_name) == 0)
+          && strcmp (func_name, exception_info->catch_exception_sym) == 0)
         break; /* We found the frame we were looking for...  */
       fi = get_prev_frame (fi);
     }
@@ -9135,7 +9266,7 @@ ada_exception_name_addr_1 (enum exception_catchpoint_kind ex,
         break;
 
       case ex_catch_exception_unhandled:
-        return ada_unhandled_exception_name_addr ();
+        return exception_info->unhandled_exception_name_addr ();
         break;
       
       case ex_catch_assert:
@@ -9386,39 +9517,6 @@ ada_exception_catchpoint_p (struct breakpoint *b)
           || b->ops == &catch_assert_breakpoint_ops);
 }
 
-/* Cause the appropriate error if no appropriate runtime symbol is
-   found to set a breakpoint, using ERR_DESC to describe the
-   breakpoint.  */
-
-static void
-error_breakpoint_runtime_sym_not_found (const char *err_desc)
-{
-  /* If we are not debugging an Ada program, we cannot put exception
-     catchpoints!  */
-
-  if (ada_update_initial_language (language_unknown, NULL) != language_ada)
-    error (_("Unable to break on %s.  Is this an Ada main program?"),
-           err_desc);
-
-  /* If the symbol does not exist, then check that the program is
-     already started, to make sure that shared libraries have been
-     loaded.  If it is not started, this may mean that the symbol is
-     in a shared library.  */
-
-  if (ptid_get_pid (inferior_ptid) == 0)
-    error (_("Unable to break on %s. Try to start the program first."),
-           err_desc);
-
-  /* At this point, we know that we are debugging an Ada program and
-     that the inferior has been started, but we still are not able to
-     find the run-time symbols. That can mean that we are in
-     configurable run time mode, or that a-except as been optimized
-     out by the linker...  In any case, at this point it is not worth
-     supporting this feature.  */
-
-  error (_("Cannot break on %s in this configuration."), err_desc);
-}
-
 /* Return a newly allocated copy of the first space-separated token
    in ARGSP, and then adjust ARGSP to point immediately after that
    token.
@@ -9512,16 +9610,18 @@ catch_ada_exception_command_split (char *args,
 static const char *
 ada_exception_sym_name (enum exception_catchpoint_kind ex)
 {
+  gdb_assert (exception_info != NULL);
+
   switch (ex)
     {
       case ex_catch_exception:
-        return (raise_sym_name);
+        return (exception_info->catch_exception_sym);
         break;
       case ex_catch_exception_unhandled:
-        return (raise_unhandled_sym_name);
+        return (exception_info->catch_exception_unhandled_sym);
         break;
       case ex_catch_assert:
-        return (raise_assert_sym_name);
+        return (exception_info->catch_assert_sym);
         break;
       default:
         internal_error (__FILE__, __LINE__,
@@ -9600,7 +9700,10 @@ ada_exception_sal (enum exception_catchpoint_kind ex, char *exp_string,
   struct symbol *sym;
   struct symtab_and_line sal;
 
-  /* First lookup the function on which we will break in order to catch
+  /* First, find out which exception support info to use.  */
+  ada_exception_support_info_sniffer ();
+
+  /* Then lookup the function on which we will break in order to catch
      the Ada exceptions requested by the user.  */
 
   sym_name = ada_exception_sym_name (ex);
@@ -9624,7 +9727,7 @@ ada_exception_sal (enum exception_catchpoint_kind ex, char *exp_string,
      this case for now.  */
 
   if (sym == NULL)
-    error_breakpoint_runtime_sym_not_found (sym_name);
+    error (_("Unable to break on '%s' in this configuration."), sym_name);
 
   /* Make sure that the symbol we found corresponds to a function.  */
   if (SYMBOL_CLASS (sym) != LOC_BLOCK)