[RFA] Add support for catch Ada exceptions (take 2)

Joel Brobecker brobecker@adacore.com
Thu Jan 4 05:32:00 GMT 2007


> This is OK with just two things: I mentioned xstrprintf to avoid
> duplicating strings in another message around when you posted this,
> and some calls to error don't use _().

Thanks. I made the fixes you pointed out, and also noticed that
ada_exception_catchpoint_cond_string could be rewritten advantageously
using xstrprintf as well, so I did that.

Here is what I ended up checkin in:

2007-01-04  Joel Brobecker  <brobecker@adacore.com>

        * ada-lang.h (ada_find_printable_frame): Remove.
        (ada_exception_catchpoint_p, ada_decode_exception_location)
        (ada_decode_assert_location): Add declaration.
        * ada-lang.c: Add include of annotate.h and valprint.h.
        (exception_catchpoint_kind): New enum.
        (function_name_from_pc, is_known_support_routine)
        (ada_find_printable_frame, ada_unhandled_exception_name_addr)
        (ada_exception_name_addr_1, ada_exception_name_addr)
        (print_it_exception, print_one_exception, print_mention_exception)
        (print_it_catch_exception, print_one_catch_exception)
        (print_mention_catch_exception, catch_exception_breakpoint_ops)
        (print_it_catch_exception_unhandled)
        (print_one_catch_exception_unhandled)
        (print_mention_catch_exception_unhandled, print_it_catch_assert)
        (print_one_catch_assert, print_mention_catch_assert)
        (ada_exception_catchpoint_p, error_breakpoint_runtime_sym_not_found)
        (ada_get_next_arg, catch_ada_exception_command_split)
        (ada_exception_sym_name, ada_exception_sym_name)
        (ada_exception_breakption_ops, ada_exception_catchpoint_cond_string)
        (ada_parse_catchpoint_condition, ada_exception_sal)
        (ada_decode_exception_location)
        (ada_decode_assert_location): New function.
        (catch_exception_unhandled_breakpoint_ops): New global variable.
        (catch_assert_breakpoint_ops): New global variable.
        * breakpoint.c: Add include of ada-lang.h.
        (print_one_breakpoint): Do not print the condition for Ada
        exception catchpoints.
        (create_ada_exception_breakpoint): New function.
        (catch_ada_exception_command, catch_assert_command): New function.
        (catch_command_1): Add support for the new "catch exception" and
        "catch assert" commands.
        (_initialize_breakpoint): Add help description for the new catch
        commands.
        * Makefile.in (ada-lang.o): Add dependency on annotate.h and
        valprint.h.
        (breakpoint.o): Add dependency on ada-lang.h.

The testcase will be committed shortly. I'll follow with NEWS and docs
right after that.

Thanks again for the review. I'll try to schedule some time this weekend
to improve is_known_support_routine.

-- 
Joel
-------------- next part --------------
Index: ada-lang.h
===================================================================
RCS file: /cvs/src/src/gdb/ada-lang.h,v
retrieving revision 1.23
diff -u -p -r1.23 ada-lang.h
--- ada-lang.h	12 Jan 2006 08:36:29 -0000	1.23
+++ ada-lang.h	4 Jan 2007 05:24:55 -0000
@@ -1,7 +1,8 @@
 /* Ada language support definitions for GDB, the GNU debugger.
 
    Copyright (C) 1992, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005 Free Software Foundation, Inc.
+   2005, 2007
+   Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -470,8 +471,6 @@ extern int ada_print_exception_breakpoin
 
 extern void ada_print_exception_breakpoint_task (struct breakpoint *);
 
-extern void ada_find_printable_frame (struct frame_info *fi);
-
 extern void ada_reset_thread_registers (void);
 
 extern int ada_build_task_list (void);
@@ -486,4 +485,18 @@ extern struct symbol *lookup_symbol_in_l
 						 enum language,
 						 int *,
 						 struct symtab **);
+
+extern int ada_exception_catchpoint_p (struct breakpoint *b);
+  
+extern struct symtab_and_line
+  ada_decode_exception_location (char *args, char **addr_string,
+                                 char **exp_string, char **cond_string,
+                                 struct expression **cond,
+                                 struct breakpoint_ops **ops);
+
+extern struct symtab_and_line
+  ada_decode_assert_location (char *args, char **addr_string,
+                              struct breakpoint_ops **ops);
+
+
 #endif
Index: ada-lang.c
===================================================================
RCS file: /cvs/src/src/gdb/ada-lang.c,v
retrieving revision 1.86
diff -u -p -r1.86 ada-lang.c
--- ada-lang.c	3 Jan 2007 19:01:25 -0000	1.86
+++ ada-lang.c	4 Jan 2007 05:25:14 -0000
@@ -1,7 +1,7 @@
 /* Ada language support routines for GDB, the GNU debugger.  Copyright (C)
 
-   1992, 1993, 1994, 1997, 1998, 1999, 2000, 2003, 2004, 2005 Free
-   Software Foundation, Inc.
+   1992, 1993, 1994, 1997, 1998, 1999, 2000, 2003, 2004, 2005, 2007
+   Free Software Foundation, Inc.
 
 This file is part of GDB.
 
@@ -53,6 +53,8 @@ Boston, MA 02110-1301, USA.  */
 #include "infcall.h"
 #include "dictionary.h"
 #include "exceptions.h"
+#include "annotate.h"
+#include "valprint.h"
 
 #ifndef ADA_RETAIN_DOTS
 #define ADA_RETAIN_DOTS 0
@@ -8973,6 +8975,786 @@ ada_modulus (struct type * type)
   return (ULONGEST) TYPE_HIGH_BOUND (type) + 1;
 }
 
+
+/* Ada exception catchpoint support:
+   ---------------------------------
+
+   We support 3 kinds of exception catchpoints:
+     . catchpoints on Ada exceptions
+     . catchpoints on unhandled Ada exceptions
+     . catchpoints on failed assertions
+
+   Exceptions raised during failed assertions, or unhandled exceptions
+   could perfectly be caught with the general catchpoint on Ada exceptions.
+   However, we can easily differentiate these two special cases, and having
+   the option to distinguish these two cases from the rest can be useful
+   to zero-in on certain situations.
+
+   Exception catchpoints are a specialized form of breakpoint,
+   since they rely on inserting breakpoints inside known routines
+   of the GNAT runtime.  The implementation therefore uses a standard
+   breakpoint structure of the BP_BREAKPOINT type, but with its own set
+   of breakpoint_ops.
+
+   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).
+   
+   Conditions where EXP_STRING, COND, and COND_STRING are used:
+
+     When a user specifies the name of a specific exception in the case
+     of catchpoints on Ada exceptions, we store the name of that exception
+     in the EXP_STRING.  We then translate this request into an actual
+     condition stored in COND_STRING, and then parse it into an expression
+     stored in COND.  */
+
+/* The different types of catchpoints that we introduced for catching
+   Ada exceptions.  */
+
+enum exception_catchpoint_kind
+{
+  ex_catch_exception,
+  ex_catch_exception_unhandled,
+  ex_catch_assert
+};
+
+/* 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.  */
+
+static char *
+function_name_from_pc (CORE_ADDR pc)
+{
+  char *func_name;
+
+  if (!find_pc_partial_function (pc, &func_name, NULL, NULL))
+    return NULL;
+
+  return func_name;
+}
+
+/* True iff FRAME is very likely to be that of a function that is
+   part of the runtime system.  This is all very heuristic, but is
+   intended to be used as advice as to what frames are uninteresting
+   to most users.  */
+
+static int
+is_known_support_routine (struct frame_info *frame)
+{
+  struct frame_info *next_frame = get_next_frame (frame);
+  /* If frame is not innermost, that normally means that frame->pc
+     points to *after* the call instruction, and we want to get the line
+     containing the call, never the next line.  But if the next frame is
+     a signal_handler_caller or a dummy frame, then the next frame was
+     not entered as the result of a call, and we want to get the line
+     containing frame->pc.  */
+  const int pc_is_after_call =
+    next_frame != NULL
+    && get_frame_type (next_frame) != SIGTRAMP_FRAME
+    && get_frame_type (next_frame) != DUMMY_FRAME;
+  struct symtab_and_line sal
+    = find_pc_line (get_frame_pc (frame), pc_is_after_call);
+  char *func_name;
+  int i;
+  struct stat st;
+
+  /* The heuristic:
+     1. The symtab is null (indicating no debugging symbols)
+     2. The symtab's filename does not exist.
+     3. The object file's name is one of the standard libraries.
+     4. The symtab's file name has the form of an Ada library source file.
+     5. The function at frame's PC has a GNAT-compiler-generated name.  */
+
+  if (sal.symtab == NULL)
+    return 1;
+
+  /* On some systems (e.g. VxWorks), the kernel contains debugging
+     symbols; in this case, the filename referenced by these symbols
+     does not exists.  */
+
+  if (stat (sal.symtab->filename, &st))
+    return 1;
+
+  for (i = 0; known_runtime_file_name_patterns[i] != NULL; i += 1)
+    {
+      re_comp (known_runtime_file_name_patterns[i]);
+      if (re_exec (sal.symtab->filename))
+        return 1;
+    }
+  if (sal.symtab->objfile != NULL)
+    {
+      for (i = 0; known_runtime_file_name_patterns[i] != NULL; i += 1)
+        {
+          re_comp (known_runtime_file_name_patterns[i]);
+          if (re_exec (sal.symtab->objfile->name))
+            return 1;
+        }
+    }
+
+  /* If the frame PC points after the call instruction, then we need to
+     decrement it in order to search for the function associated to this
+     PC.  Otherwise, if the associated call was the last instruction of
+     the function, we might either find the wrong function or even fail
+     during the function name lookup.  */
+  if (pc_is_after_call)
+    func_name = function_name_from_pc (get_frame_pc (frame) - 1);
+  else
+    func_name = function_name_from_pc (get_frame_pc (frame));
+
+  if (func_name == NULL)
+    return 1;
+
+  for (i = 0; known_auxiliary_function_name_patterns[i] != NULL; i += 1)
+    {
+      re_comp (known_auxiliary_function_name_patterns[i]);
+      if (re_exec (func_name))
+        return 1;
+    }
+
+  return 0;
+}
+
+/* Find the first frame that contains debugging information and that is not
+   part of the Ada run-time, starting from FI and moving upward.  */
+
+static void
+ada_find_printable_frame (struct frame_info *fi)
+{
+  for (; fi != NULL; fi = get_prev_frame (fi))
+    {
+      if (!is_known_support_routine (fi))
+        {
+          select_frame (fi);
+          break;
+        }
+    }
+
+}
+
+/* Assuming that the inferior just triggered an unhandled exception
+   catchpoint, return the address in inferior memory where the name
+   of the exception is stored.
+   
+   Return zero if the address could not be computed.  */
+
+static CORE_ADDR
+ada_unhandled_exception_name_addr (void)
+{
+  int frame_level;
+  struct frame_info *fi;
+
+  /* To determine the name of this exception, we need to select
+     the frame corresponding to RAISE_SYM_NAME.  This frame is
+     at least 3 levels up, so we simply skip the first 3 frames
+     without checking the name of their associated function.  */
+  fi = get_current_frame ();
+  for (frame_level = 0; frame_level < 3; frame_level += 1)
+    if (fi != NULL)
+      fi = get_prev_frame (fi); 
+
+  while (fi != NULL)
+    {
+      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)
+        break; /* We found the frame we were looking for...  */
+      fi = get_prev_frame (fi);
+    }
+
+  if (fi == NULL)
+    return 0;
+
+  select_frame (fi);
+  return parse_and_eval_address ("id.full_name");
+}
+
+/* Assuming the inferior just triggered an Ada exception catchpoint
+   (of any type), return the address in inferior memory where the name
+   of the exception is stored, if applicable.
+
+   Return zero if the address could not be computed, or if not relevant.  */
+
+static CORE_ADDR
+ada_exception_name_addr_1 (enum exception_catchpoint_kind ex,
+                           struct breakpoint *b)
+{
+  switch (ex)
+    {
+      case ex_catch_exception:
+        return (parse_and_eval_address ("e.full_name"));
+        break;
+
+      case ex_catch_exception_unhandled:
+        return ada_unhandled_exception_name_addr ();
+        break;
+      
+      case ex_catch_assert:
+        return 0;  /* Exception name is not relevant in this case.  */
+        break;
+
+      default:
+        internal_error (__FILE__, __LINE__, _("unexpected catchpoint type"));
+        break;
+    }
+
+  return 0; /* Should never be reached.  */
+}
+
+/* Same as ada_exception_name_addr_1, except that it intercepts and contains
+   any error that ada_exception_name_addr_1 might cause to be thrown.
+   When an error is intercepted, a warning with the error message is printed,
+   and zero is returned.  */
+
+static CORE_ADDR
+ada_exception_name_addr (enum exception_catchpoint_kind ex,
+                         struct breakpoint *b)
+{
+  struct gdb_exception e;
+  CORE_ADDR result = 0;
+
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      result = ada_exception_name_addr_1 (ex, b);
+    }
+
+  if (e.reason < 0)
+    {
+      warning (_("failed to get exception name: %s"), e.message);
+      return 0;
+    }
+
+  return result;
+}
+
+/* Implement the PRINT_IT method in the breakpoint_ops structure
+   for all exception catchpoint kinds.  */
+
+static enum print_stop_action
+print_it_exception (enum exception_catchpoint_kind ex, struct breakpoint *b)
+{
+  const CORE_ADDR addr = ada_exception_name_addr (ex, b);
+  char exception_name[256];
+
+  if (addr != 0)
+    {
+      read_memory (addr, exception_name, sizeof (exception_name) - 1);
+      exception_name [sizeof (exception_name) - 1] = '\0';
+    }
+
+  ada_find_printable_frame (get_current_frame ());
+
+  annotate_catchpoint (b->number);
+  switch (ex)
+    {
+      case ex_catch_exception:
+        if (addr != 0)
+          printf_filtered (_("\nCatchpoint %d, %s at "),
+                           b->number, exception_name);
+        else
+          printf_filtered (_("\nCatchpoint %d, exception at "), b->number);
+        break;
+      case ex_catch_exception_unhandled:
+        if (addr != 0)
+          printf_filtered (_("\nCatchpoint %d, unhandled %s at "),
+                           b->number, exception_name);
+        else
+          printf_filtered (_("\nCatchpoint %d, unhandled exception at "),
+                           b->number);
+        break;
+      case ex_catch_assert:
+        printf_filtered (_("\nCatchpoint %d, failed assertion at "),
+                         b->number);
+        break;
+    }
+
+  return PRINT_SRC_AND_LOC;
+}
+
+/* Implement the PRINT_ONE method in the breakpoint_ops structure
+   for all exception catchpoint kinds.  */
+
+static void
+print_one_exception (enum exception_catchpoint_kind ex,
+                     struct breakpoint *b, CORE_ADDR *last_addr)
+{ 
+  if (addressprint)
+    {
+      annotate_field (4);
+      ui_out_field_core_addr (uiout, "addr", b->loc->address);
+    }
+
+  annotate_field (5);
+  *last_addr = b->loc->address;
+  switch (ex)
+    {
+      case ex_catch_exception:
+        if (b->exp_string != NULL)
+          {
+            char *msg = xstrprintf (_("`%s' Ada exception"), b->exp_string);
+            
+            ui_out_field_string (uiout, "what", msg);
+            xfree (msg);
+          }
+        else
+          ui_out_field_string (uiout, "what", "all Ada exceptions");
+        
+        break;
+
+      case ex_catch_exception_unhandled:
+        ui_out_field_string (uiout, "what", "unhandled Ada exceptions");
+        break;
+      
+      case ex_catch_assert:
+        ui_out_field_string (uiout, "what", "failed Ada assertions");
+        break;
+
+      default:
+        internal_error (__FILE__, __LINE__, _("unexpected catchpoint type"));
+        break;
+    }
+}
+
+/* Implement the PRINT_MENTION method in the breakpoint_ops structure
+   for all exception catchpoint kinds.  */
+
+static void
+print_mention_exception (enum exception_catchpoint_kind ex,
+                         struct breakpoint *b)
+{
+  switch (ex)
+    {
+      case ex_catch_exception:
+        if (b->exp_string != NULL)
+          printf_filtered (_("Catchpoint %d: `%s' Ada exception"),
+                           b->number, b->exp_string);
+        else
+          printf_filtered (_("Catchpoint %d: all Ada exceptions"), b->number);
+        
+        break;
+
+      case ex_catch_exception_unhandled:
+        printf_filtered (_("Catchpoint %d: unhandled Ada exceptions"),
+                         b->number);
+        break;
+      
+      case ex_catch_assert:
+        printf_filtered (_("Catchpoint %d: failed Ada assertions"), b->number);
+        break;
+
+      default:
+        internal_error (__FILE__, __LINE__, _("unexpected catchpoint type"));
+        break;
+    }
+}
+
+/* Virtual table for "catch exception" breakpoints.  */
+
+static enum print_stop_action
+print_it_catch_exception (struct breakpoint *b)
+{
+  return print_it_exception (ex_catch_exception, b);
+}
+
+static void
+print_one_catch_exception (struct breakpoint *b, CORE_ADDR *last_addr)
+{
+  print_one_exception (ex_catch_exception, b, last_addr);
+}
+
+static void
+print_mention_catch_exception (struct breakpoint *b)
+{
+  print_mention_exception (ex_catch_exception, b);
+}
+
+static struct breakpoint_ops catch_exception_breakpoint_ops =
+{
+  print_it_catch_exception,
+  print_one_catch_exception,
+  print_mention_catch_exception
+};
+
+/* Virtual table for "catch exception unhandled" breakpoints.  */
+
+static enum print_stop_action
+print_it_catch_exception_unhandled (struct breakpoint *b)
+{
+  return print_it_exception (ex_catch_exception_unhandled, b);
+}
+
+static void
+print_one_catch_exception_unhandled (struct breakpoint *b, CORE_ADDR *last_addr)
+{
+  print_one_exception (ex_catch_exception_unhandled, b, last_addr);
+}
+
+static void
+print_mention_catch_exception_unhandled (struct breakpoint *b)
+{
+  print_mention_exception (ex_catch_exception_unhandled, b);
+}
+
+static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = {
+  print_it_catch_exception_unhandled,
+  print_one_catch_exception_unhandled,
+  print_mention_catch_exception_unhandled
+};
+
+/* Virtual table for "catch assert" breakpoints.  */
+
+static enum print_stop_action
+print_it_catch_assert (struct breakpoint *b)
+{
+  return print_it_exception (ex_catch_assert, b);
+}
+
+static void
+print_one_catch_assert (struct breakpoint *b, CORE_ADDR *last_addr)
+{
+  print_one_exception (ex_catch_assert, b, last_addr);
+}
+
+static void
+print_mention_catch_assert (struct breakpoint *b)
+{
+  print_mention_exception (ex_catch_assert, b);
+}
+
+static struct breakpoint_ops catch_assert_breakpoint_ops = {
+  print_it_catch_assert,
+  print_one_catch_assert,
+  print_mention_catch_assert
+};
+
+/* Return non-zero if B is an Ada exception catchpoint.  */
+
+int
+ada_exception_catchpoint_p (struct breakpoint *b)
+{
+  return (b->ops == &catch_exception_breakpoint_ops
+          || b->ops == &catch_exception_unhandled_breakpoint_ops
+          || 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.
+
+   Return NULL if ARGPS does not contain any more tokens.  */
+
+static char *
+ada_get_next_arg (char **argsp)
+{
+  char *args = *argsp;
+  char *end;
+  char *result;
+
+  /* Skip any leading white space.  */
+
+  while (isspace (*args))
+    args++;
+
+  if (args[0] == '\0')
+    return NULL; /* No more arguments.  */
+  
+  /* Find the end of the current argument.  */
+
+  end = args;
+  while (*end != '\0' && !isspace (*end))
+    end++;
+
+  /* Adjust ARGSP to point to the start of the next argument.  */
+
+  *argsp = end;
+
+  /* Make a copy of the current argument and return it.  */
+
+  result = xmalloc (end - args + 1);
+  strncpy (result, args, end - args);
+  result[end - args] = '\0';
+  
+  return result;
+}
+
+/* Split the arguments specified in a "catch exception" command.  
+   Set EX to the appropriate catchpoint type.
+   Set EXP_STRING to the name of the specific exception if
+   specified by the user.  */
+
+static void
+catch_ada_exception_command_split (char *args,
+                                   enum exception_catchpoint_kind *ex,
+                                   char **exp_string)
+{
+  struct cleanup *old_chain = make_cleanup (null_cleanup, NULL);
+  char *exception_name;
+
+  exception_name = ada_get_next_arg (&args);
+  make_cleanup (xfree, exception_name);
+
+  /* Check that we do not have any more arguments.  Anything else
+     is unexpected.  */
+
+  while (isspace (*args))
+    args++;
+
+  if (args[0] != '\0')
+    error (_("Junk at end of expression"));
+
+  discard_cleanups (old_chain);
+
+  if (exception_name == NULL)
+    {
+      /* Catch all exceptions.  */
+      *ex = ex_catch_exception;
+      *exp_string = NULL;
+    }
+  else if (strcmp (exception_name, "unhandled") == 0)
+    {
+      /* Catch unhandled exceptions.  */
+      *ex = ex_catch_exception_unhandled;
+      *exp_string = NULL;
+    }
+  else
+    {
+      /* Catch a specific exception.  */
+      *ex = ex_catch_exception;
+      *exp_string = exception_name;
+    }
+}
+
+/* Return the name of the symbol on which we should break in order to
+   implement a catchpoint of the EX kind.  */
+
+static const char *
+ada_exception_sym_name (enum exception_catchpoint_kind ex)
+{
+  switch (ex)
+    {
+      case ex_catch_exception:
+        return (raise_sym_name);
+        break;
+      case ex_catch_exception_unhandled:
+        return (raise_unhandled_sym_name);
+        break;
+      case ex_catch_assert:
+        return (raise_assert_sym_name);
+        break;
+      default:
+        internal_error (__FILE__, __LINE__,
+                        _("unexpected catchpoint kind (%d)"), ex);
+    }
+}
+
+/* Return the breakpoint ops "virtual table" used for catchpoints
+   of the EX kind.  */
+
+static struct breakpoint_ops *
+ada_exception_breakption_ops (enum exception_catchpoint_kind ex)
+{
+  switch (ex)
+    {
+      case ex_catch_exception:
+        return (&catch_exception_breakpoint_ops);
+        break;
+      case ex_catch_exception_unhandled:
+        return (&catch_exception_unhandled_breakpoint_ops);
+        break;
+      case ex_catch_assert:
+        return (&catch_assert_breakpoint_ops);
+        break;
+      default:
+        internal_error (__FILE__, __LINE__,
+                        _("unexpected catchpoint kind (%d)"), ex);
+    }
+}
+
+/* Return the condition that will be used to match the current exception
+   being raised with the exception that the user wants to catch.  This
+   assumes that this condition is used when the inferior just triggered
+   an exception catchpoint.
+   
+   The string returned is a newly allocated string that needs to be
+   deallocated later.  */
+
+static char *
+ada_exception_catchpoint_cond_string (const char *exp_string)
+{
+  return xstrprintf ("long_integer (e) = long_integer (&%s)", exp_string);
+}
+
+/* Return the expression corresponding to COND_STRING evaluated at SAL.  */
+
+static struct expression *
+ada_parse_catchpoint_condition (char *cond_string,
+                                struct symtab_and_line sal)
+{
+  return (parse_exp_1 (&cond_string, block_for_pc (sal.pc), 0));
+}
+
+/* Return the symtab_and_line that should be used to insert an exception
+   catchpoint of the TYPE kind.
+
+   EX_STRING should contain the name of a specific exception
+   that the catchpoint should catch, or NULL otherwise.
+
+   The idea behind all the remaining parameters is that their names match
+   the name of certain fields in the breakpoint structure that are used to
+   handle exception catchpoints.  This function returns the value to which
+   these fields should be set, depending on the type of catchpoint we need
+   to create.
+   
+   If COND and COND_STRING are both non-NULL, any value they might
+   hold will be free'ed, and then replaced by newly allocated ones.
+   These parameters are left untouched otherwise.  */
+
+static struct symtab_and_line
+ada_exception_sal (enum exception_catchpoint_kind ex, char *exp_string,
+                   char **addr_string, char **cond_string,
+                   struct expression **cond, struct breakpoint_ops **ops)
+{
+  const char *sym_name;
+  struct symbol *sym;
+  struct symtab_and_line sal;
+
+  /* First 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);
+  sym = standard_lookup (sym_name, NULL, VAR_DOMAIN);
+
+  /* The symbol we're looking up is provided by a unit in the GNAT runtime
+     that should be compiled with debugging information.  As a result, we
+     expect to find that symbol in the symtabs.  If we don't find it, then
+     the target most likely does not support Ada exceptions, or we cannot
+     insert exception breakpoints yet, because the GNAT runtime hasn't been
+     loaded yet.  */
+
+  /* brobecker/2006-12-26: It is conceivable that the runtime was compiled
+     in such a way that no debugging information is produced for the symbol
+     we are looking for.  In this case, we could search the minimal symbols
+     as a fall-back mechanism.  This would still be operating in degraded
+     mode, however, as we would still be missing the debugging information
+     that is needed in order to extract the name of the exception being
+     raised (this name is printed in the catchpoint message, and is also
+     used when trying to catch a specific exception).  We do not handle
+     this case for now.  */
+
+  if (sym == NULL)
+    error_breakpoint_runtime_sym_not_found (sym_name);
+
+  /* Make sure that the symbol we found corresponds to a function.  */
+  if (SYMBOL_CLASS (sym) != LOC_BLOCK)
+    error (_("Symbol \"%s\" is not a function (class = %d)"),
+           sym_name, SYMBOL_CLASS (sym));
+
+  sal = find_function_start_sal (sym, 1);
+
+  /* Set ADDR_STRING.  */
+
+  *addr_string = xstrdup (sym_name);
+
+  /* Set the COND and COND_STRING (if not NULL).  */
+
+  if (cond_string != NULL && cond != NULL)
+    {
+      if (*cond_string != NULL)
+        {
+          xfree (*cond_string);
+          *cond_string = NULL;
+        }
+      if (*cond != NULL)
+        {
+          xfree (*cond);
+          *cond = NULL;
+        }
+      if (exp_string != NULL)
+        {
+          *cond_string = ada_exception_catchpoint_cond_string (exp_string);
+          *cond = ada_parse_catchpoint_condition (*cond_string, sal);
+        }
+    }
+
+  /* Set OPS.  */
+  *ops = ada_exception_breakption_ops (ex);
+
+  return sal;
+}
+
+/* Parse the arguments (ARGS) of the "catch exception" command.
+ 
+   Set TYPE to the appropriate exception catchpoint type.
+   If the user asked the catchpoint to catch only a specific
+   exception, then save the exception name in ADDR_STRING.
+
+   See ada_exception_sal for a description of all the remaining
+   function arguments of this function.  */
+
+struct symtab_and_line
+ada_decode_exception_location (char *args, char **addr_string,
+                               char **exp_string, char **cond_string,
+                               struct expression **cond,
+                               struct breakpoint_ops **ops)
+{
+  enum exception_catchpoint_kind ex;
+
+  catch_ada_exception_command_split (args, &ex, exp_string);
+  return ada_exception_sal (ex, *exp_string, addr_string, cond_string,
+                            cond, ops);
+}
+
+struct symtab_and_line
+ada_decode_assert_location (char *args, char **addr_string,
+                            struct breakpoint_ops **ops)
+{
+  /* Check that no argument where provided at the end of the command.  */
+
+  if (args != NULL)
+    {
+      while (isspace (*args))
+        args++;
+      if (*args != '\0')
+        error (_("Junk at end of arguments."));
+    }
+
+  return ada_exception_sal (ex_catch_assert, NULL, addr_string, NULL, NULL,
+                            ops);
+}
+
                                 /* Operators */
 /* Information about operators given special treatment in functions
    below.  */
Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.236
diff -u -p -r1.236 breakpoint.c
--- breakpoint.c	3 Jan 2007 19:01:25 -0000	1.236
+++ breakpoint.c	4 Jan 2007 05:25:30 -0000
@@ -1,7 +1,8 @@
 /* Everything about breakpoints, for GDB.
 
    Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
-   1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+   2007
    Free Software Foundation, Inc.
 
    This file is part of GDB.
@@ -54,6 +55,7 @@
 #include "observer.h"
 #include "exceptions.h"
 #include "memattr.h"
+#include "ada-lang.h"
 
 #include "gdb-events.h"
 #include "mi/mi-common.h"
@@ -3609,8 +3611,11 @@ print_one_breakpoint (struct breakpoint 
       ui_out_text (uiout, "\n");
     }
   
-  if (b->cond)
+  if (b->cond && !ada_exception_catchpoint_p (b))
     {
+      /* We do not print the condition for Ada exception catchpoints
+         because the condition is an internal implementation detail
+         that we do not want to expose to the user.  */
       annotate_field (7);
       ui_out_text (uiout, "\tstop only if ");
       print_expression (b->cond, stb->stream);
@@ -6507,6 +6512,86 @@ catch_exception_command_1 (enum exceptio
   warning (_("Unsupported with this platform/compiler combination."));
 }
 
+/* Create a breakpoint struct for Ada exception catchpoints.  */
+
+static void
+create_ada_exception_breakpoint (struct symtab_and_line sal,
+                                 char *addr_string,
+                                 char *exp_string,
+                                 char *cond_string,
+                                 struct expression *cond,
+                                 struct breakpoint_ops *ops,
+                                 int tempflag,
+                                 int from_tty)
+{
+  struct breakpoint *b;
+
+  if (from_tty)
+    {
+      describe_other_breakpoints (sal.pc, sal.section, -1);
+      /* FIXME: brobecker/2006-12-28: Actually, re-implement a special
+         version for exception catchpoints, because two catchpoints
+         used for different exception names will use the same address.
+         In this case, a "breakpoint ... also set at..." warning is
+         unproductive.  Besides. the warning phrasing is also a bit
+         inapropriate, we should use the word catchpoint, and tell
+         the user what type of catchpoint it is.  The above is good
+         enough for now, though.  */
+    }
+
+  b = set_raw_breakpoint (sal, bp_breakpoint);
+  set_breakpoint_count (breakpoint_count + 1);
+
+  b->enable_state = bp_enabled;
+  b->disposition = tempflag ? disp_del : disp_donttouch;
+  b->number = breakpoint_count;
+  b->ignore_count = 0;
+  b->cond = cond;
+  b->addr_string = addr_string;
+  b->language = language_ada;
+  b->cond_string = cond_string;
+  b->exp_string = exp_string;
+  b->thread = -1;
+  b->ops = ops;
+  b->from_tty = from_tty;
+
+  mention (b);
+}
+
+/* Implement the "catch exception" command.  */
+
+static void
+catch_ada_exception_command (char *arg, int tempflag, int from_tty)
+{
+  struct symtab_and_line sal;
+  enum bptype type;
+  char *addr_string = NULL;
+  char *exp_string = NULL;
+  char *cond_string = NULL;
+  struct expression *cond = NULL;
+  struct breakpoint_ops *ops = NULL;
+
+  sal = ada_decode_exception_location (arg, &addr_string, &exp_string,
+                                       &cond_string, &cond, &ops);
+  create_ada_exception_breakpoint (sal, addr_string, exp_string,
+                                   cond_string, cond, ops, tempflag,
+                                   from_tty);
+}
+
+/* Implement the "catch assert" command.  */
+
+static void
+catch_assert_command (char *arg, int tempflag, int from_tty)
+{
+  struct symtab_and_line sal;
+  char *addr_string = NULL;
+  struct breakpoint_ops *ops = NULL;
+
+  sal = ada_decode_assert_location (arg, &addr_string, &ops);
+  create_ada_exception_breakpoint (sal, addr_string, NULL, NULL, NULL, ops,
+                                   tempflag, from_tty);
+}
+
 /* Cover routine to allow wrapping target_enable_exception_catchpoints
    inside a catch_errors */
 
@@ -6611,6 +6696,15 @@ catch_command_1 (char *arg, int tempflag
     {
       error (_("Catch of stop not yet implemented"));
     }
+  else if (strncmp (arg1_start, "exception", arg1_length) == 0)
+    {
+      catch_ada_exception_command (arg1_end + 1, tempflag, from_tty);
+    }
+
+  else if (strncmp (arg1_start, "assert", arg1_length) == 0)
+    {
+      catch_assert_command (arg1_end + 1, tempflag, from_tty);
+    }
 
   /* This doesn't appear to be an event name */
 
@@ -8117,6 +8211,11 @@ The act of your program's execution stop
 C++ exceptions may be caught:\n\
 \tcatch throw               - all exceptions, when thrown\n\
 \tcatch catch               - all exceptions, when caught\n\
+Ada exceptions may be caught:\n\
+\tcatch exception           - all exceptions, when raised\n\
+\tcatch exception <name>    - a particular exception, when raised\n\
+\tcatch exception unhandled - all unhandled exceptions, when raised\n\
+\tcatch assert              - all failed assertions, when raised\n\
 \n\
 Do \"help set follow-fork-mode\" for info on debugging your program\n\
 after a fork or vfork is caught.\n\n\
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.865
diff -u -p -r1.865 Makefile.in
--- Makefile.in	3 Jan 2007 21:46:11 -0000	1.865
+++ Makefile.in	4 Jan 2007 05:25:37 -0000
@@ -1,5 +1,5 @@
 # Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997,
-# 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+# 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
 # Free Software Foundation, Inc.
 
 # This file is part of GDB.
@@ -1694,7 +1694,7 @@ ada-lang.o: ada-lang.c $(defs_h) $(gdb_s
 	$(inferior_h) $(symfile_h) $(objfiles_h) $(breakpoint_h) \
 	$(gdbcore_h) $(hashtab_h) $(gdb_obstack_h) $(ada_lang_h) \
 	$(completer_h) $(gdb_stat_h) $(ui_out_h) $(block_h) $(infcall_h) \
-	$(dictionary_h) $(exceptions_h)
+	$(dictionary_h) $(exceptions_h) $(annotate_h) $(valprint_h)
 ada-typeprint.o: ada-typeprint.c $(defs_h) $(gdb_obstack_h) $(bfd_h) \
 	$(symtab_h) $(gdbtypes_h) $(expression_h) $(value_h) $(gdbcore_h) \
 	$(target_h) $(command_h) $(gdbcmd_h) $(language_h) $(demangle_h) \
@@ -1834,7 +1834,7 @@ breakpoint.o: breakpoint.c $(defs_h) $(s
 	$(objfiles_h) $(source_h) $(linespec_h) $(completer_h) $(gdb_h) \
 	$(ui_out_h) $(cli_script_h) $(gdb_assert_h) $(block_h) $(solib_h) \
 	$(solist_h) $(observer_h) $(exceptions_h) $(gdb_events_h) $(mi_common_h) \
-	$(memattr_h)
+	$(memattr_h) $(ada_lang_h)
 bsd-kvm.o: bsd-kvm.c $(defs_h) $(cli_cmds_h) $(command_h) $(frame_h) \
 	$(regcache_h) $(target_h) $(value_h) $(gdbcore_h) $(gdb_assert_h) \
 	$(readline_h) $(bsd_kvm_h)


More information about the Gdb-patches mailing list