Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Joel Brobecker <brobecker@adacore.com>
To: gdb-patches@sourceware.org
Subject: Re: [RFA] Add support for catch Ada exceptions (take 2)
Date: Tue, 02 Jan 2007 06:15:00 -0000	[thread overview]
Message-ID: <20070102061526.GH17211@adacore.com> (raw)
In-Reply-To: <20070102060712.GG17211@adacore.com>

[-- Attachment #1: Type: text/plain, Size: 4409 bytes --]

[humpf. With the patch this time :-(]

> > Hiding the condition is not a big deal. If needed, I can use a bit
> > from the breakpoint flag, don't you think.
> 
> I didn't even know that was there.  Yes, that would be fine.  I would
> recommend separating it into a bitfield, too.

Actually, I found a way without using the flag. I figured using a flag
might be convenient, but is is redundant. I can identify the catchpoints
using their OPS field!

Here is a new iteration of the patch that should be much better. As you
predicted, it made most of the changes to breakpoint.c go away, at a
slight cost in ada-lang.c. The size of the patch is almost identical,
but most of it is contained in one place inside ada-lang.o. Perhaps
we should split this off into a different file, even, but I think this
would make the patch even bigger for little gain for now.

It occurred to me during a last second check that is_known_support_routine
could be simplified a bit. Please do not pay attention to the pc_after_call
and decrement-pc-by-one stuff. I took this code straight of our tree and
it predates a bit all the functions we wrote (I even wrote one of them!).
It's inelegant, but it works. I'll clean this up another rainy day.
I have also taken care of the copyright years in the headers.

Apart from that, I'm pretty happy the way the exercise turned out.

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

        * ada-lang.h (ada_find_printable_frame): Remove.
        (ada_exception_catchpoint_p): Add declaration.
        (ada_decode_exception_location): Add declaration.
        (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): New function.
        (is_known_support_routine): New function.
        (ada_find_printable_frame): New function.
        (ada_unhandled_exception_name_addr): New function.
        (ada_exception_name_addr_1): New function.
        (ada_exception_name_addr): New function.
        (print_it_exception): New function.
        (print_one_exception): New function.
        (print_mention_exception): New function.
        (print_it_catch_exception): New function.
        (print_one_catch_exception): New function.
        (print_mention_catch_exception): New function.
        (catch_exception_breakpoint_ops): New global variable.
        (print_it_catch_exception_unhandled): New function.
        (print_one_catch_exception_unhandled): New function.
        (print_mention_catch_exception_unhandled): New function.
        (catch_exception_unhandled_breakpoint_ops): New global variable.
        (print_it_catch_assert): New function.
        (print_one_catch_assert): New function.
        (print_mention_catch_assert): New function.
        (catch_assert_breakpoint_ops): New global variable.
        (ada_exception_catchpoint_p): New function.
        (error_breakpoint_runtime_sym_not_found): New function.
        (ada_get_next_arg): New function.
        (catch_ada_exception_command_split): New function.
        (ada_exception_sym_name): New function.
        (ada_exception_breakption_ops): New function.
        (ada_exception_catchpoint_cond_string): New function.
        (ada_parse_catchpoint_condition): New function.
        (ada_exception_sal): New function.
        (ada_decode_exception_location): New function.
        (ada_decode_assert_location): New function.
        * 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): New function.
        (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 new testcase:

2006-01-02  Joel Brobecker  <brobecker@adacore.com>

        * gdb.ada/catch_ex/foo.adb: New file.
        * gdb.ada/catch_ex.exp: New testcase.

Tested on x86-linux. No regression.

OK to apply?  NEWS entry and documentation will follow shortly after
approval.

Thank you,
-- 
Joel

[-- Attachment #2: catch-except-v6.diff --]
[-- Type: text/plain, Size: 33207 bytes --]

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	2 Jan 2007 04:33:29 -0000
@@ -470,8 +470,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 +484,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.85
diff -u -p -r1.85 ada-lang.c
--- ada-lang.c	1 Dec 2006 00:32:29 -0000	1.85
+++ ada-lang.c	2 Jan 2007 04:33:59 -0000
@@ -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,7 +8975,793 @@ ada_modulus (struct type * type)
   return (ULONGEST) TYPE_HIGH_BOUND (type) + 1;
 }
 \f
-                                /* Operators */
+
+/* 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)
+          {
+            const char *template = _("`%s' Ada exception");
+            char *msg = alloca (strlen (template) + strlen (b->exp_string));
+            
+            sprintf (msg, _("`%s' Ada exception"), b->exp_string);
+            ui_out_field_string (uiout, "what", 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)
+{
+  const char cond_prefix[] = "long_integer (e) = long_integer (&";
+  const char cond_postfix[] = ")";
+  char *cond_string = xmalloc (sizeof (cond_prefix) + strlen (exp_string)
+                              + sizeof (cond_prefix) + 1);
+
+  sprintf (cond_string, "%s%s%s", cond_prefix, exp_string, cond_postfix);
+
+  return cond_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);
+}
+
 /* Information about operators given special treatment in functions
    below.  */
 /* Format: OP_DEFN (<operator>, <operator length>, <# args>, <binop>).  */
Index: breakpoint.c
===================================================================
RCS file: /cvs/src/src/gdb/breakpoint.c,v
retrieving revision 1.234
diff -u -p -r1.234 breakpoint.c
--- breakpoint.c	18 Dec 2006 22:10:13 -0000	1.234
+++ breakpoint.c	2 Jan 2007 04:34:14 -0000
@@ -54,6 +54,7 @@
 #include "observer.h"
 #include "exceptions.h"
 #include "memattr.h"
+#include "ada-lang.h"
 
 #include "gdb-events.h"
 #include "mi/mi-common.h"
@@ -3620,8 +3621,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);
@@ -6518,6 +6522,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 */
 
@@ -6622,6 +6706,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 */
 
@@ -8128,6 +8221,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.861
diff -u -p -r1.861 Makefile.in
--- Makefile.in	17 Dec 2006 13:30:43 -0000	1.861
+++ Makefile.in	2 Jan 2007 04:34:21 -0000
@@ -1701,7 +1701,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) \
@@ -1841,7 +1841,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)

[-- Attachment #3: foo.adb --]
[-- Type: text/plain, Size: 1186 bytes --]

--  Copyright 2007 Free Software Foundation, Inc.
--
--  This program is free software; you can redistribute it and/or modify
--  it under the terms of the GNU General Public License as published by
--  the Free Software Foundation; either version 2 of the License, or
--  (at your option) any later version.
--
--  This program is distributed in the hope that it will be useful,
--  but WITHOUT ANY WARRANTY; without even the implied warranty of
--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--  GNU General Public License for more details.
--
--  You should have received a copy of the GNU General Public License
--  along with this program; if not, write to the Free Software
--  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
--  MA 02110-1301, USA

procedure Foo is
begin

   begin
      raise Constraint_Error;  -- SPOT1
   exception
      when others =>
         null;
   end;

   begin
      raise Program_Error;  -- SPOT2
   exception
      when others =>
         null;
   end;

   begin
      pragma Assert (False);  -- SPOT3
      null;
   exception
      when others =>
         null;
   end;

   raise Constraint_Error;  -- SPOT4

end Foo;

[-- Attachment #4: catch_ex.exp --]
[-- Type: text/plain, Size: 4928 bytes --]

# Copyright 2007 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA

if $tracelevel then {
    strace $tracelevel
}

load_lib "ada.exp"

set testdir "catch_ex"
set testfile "${testdir}/foo"
set srcfile ${srcdir}/${subdir}/${testfile}.adb
set binfile ${objdir}/${subdir}/${testfile}

file mkdir ${objdir}/${subdir}/${testdir}
if {[gdb_compile_ada "${srcfile}" "${binfile}" executable [list debug additional_flags=-gnata ]] != "" } {
  return -1
}

gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir
gdb_load ${binfile}

# Some global variables used to simplify the maintenance of some of
# the regular expressions below.
set any_nb "\[0-9\]+"
set any_addr "0x\[0-9a-zA-Z\]+"
set eol "\[\r\n\]+"
set sp "\\s+"

set info_break_header "Num${sp}Type${sp}Disp${sp}Enb${sp}Address${sp}What"
set catch_exception_info \
  "$any_nb${sp}breakpoint${sp}keep${sp}y${sp}$any_addr${sp}all Ada exceptions"

####################################
# 1. Try catching all exceptions.  #
####################################

if ![runto_main] then {
   fail "Cannot run to main, testcase aborted"
   return 0
}

gdb_test "catch exception" \
         "Catchpoint $any_nb: all Ada exceptions" \
         "insert catchpoint on all Ada exceptions"

gdb_test "info break" \
         "$info_break_header$eol.*$catch_exception_info" \
         "info break, catch all Ada exceptions"

set catchpoint_msg \
  "Catchpoint $any_nb, CONSTRAINT_ERROR at $any_addr in foo \\\(\\\).*at .*foo.adb:$any_nb"
gdb_test "continue" \
         "Continuing\.$eol$catchpoint_msg$eol.*SPOT1" \
         "continuing to first exception"

set catchpoint_msg \
  "Catchpoint $any_nb, PROGRAM_ERROR at $any_addr in foo \\\(\\\).*at .*foo.adb:$any_nb"
gdb_test "continue" \
         "Continuing\.$eol$catchpoint_msg$eol.*SPOT2" \
         "continuing to second exception"

################################################
# 2. Try catching only some of the exceptions. #
################################################

# Here is the scenario:
#  - Restart the debugger from scratch, runto_main
#  - We'll catch only "Program_Error"
#    We'll catch assertions
#    We'll catch unhandled exceptions
#  - continue, we should see the first Program_Error exception
#  - continue, we should see the failed assertion
#  - continue, we should see the unhandled Constrait_Error exception
#  - continue, the program exits.

if ![runto_main] then {
   fail "Cannot run to main, testcase aborted"
   return 0
}

gdb_test "catch exception Program_Error" \
         "Catchpoint $any_nb: \`Program_Error' Ada exception" \
         "insert catchpoint on Program_Error"

gdb_test "catch assert" \
         "Catchpoint $any_nb: failed Ada assertions" \
         "insert catchpoint on failed assertions"

gdb_test "catch exception unhandled" \
         "Catchpoint $any_nb: unhandled Ada exceptions" \
         "insert catchpoint on unhandled exceptions"

set catch_exception_entry \
  "$any_nb${sp}breakpoint${sp}keep${sp}y${sp}$any_addr${sp}\`Program_Error' Ada exception"
set catch_assert_entry \
  "$any_nb${sp}breakpoint${sp}keep${sp}y${sp}$any_addr${sp}failed Ada assertions"
set catch_unhandled_entry \
  "$any_nb${sp}breakpoint${sp}keep${sp}y${sp}$any_addr${sp}unhandled Ada exceptions"

gdb_test "info break" \
         "$info_break_header$eol.*$catch_exception_entry$eol$catch_assert_entry$eol$catch_unhandled_entry" \
         "info break, second run"

set catchpoint_msg \
  "Catchpoint $any_nb, PROGRAM_ERROR at $any_addr in foo \\\(\\\).*at .*foo.adb:$any_nb"
gdb_test "continue" \
         "Continuing\.$eol$catchpoint_msg$eol.*SPOT2" \
         "continuing to Program_Error exception"

set catchpoint_msg \
  "Catchpoint $any_nb, failed assertion at $any_addr in foo \\\(\\\).*at .*foo.adb:$any_nb"
gdb_test "continue" \
         "Continuing\.$eol$catchpoint_msg$eol.*SPOT3" \
         "continuing to Program_Error exception"

set catchpoint_msg \
  "Catchpoint $any_nb, unhandled CONSTRAINT_ERROR at $any_addr in foo \\\(\\\).*at .*foo.adb:$any_nb"
gdb_test "continue" \
         "Continuing\.$eol$catchpoint_msg$eol.*SPOT4" \
         "continuing to Program_Error exception"

gdb_test "continue" \
         "Continuing\..*Program exited.*" \
         "continuing to program completion"



  reply	other threads:[~2007-01-02  6:15 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-12-30  3:20 [RFC/RFA] Add support for catch Ada exceptions Joel Brobecker
2006-12-30 15:55 ` Eli Zaretskii
2006-12-31  4:39   ` Joel Brobecker
2006-12-31 21:37 ` Daniel Jacobowitz
2007-01-01  4:42   ` Joel Brobecker
2007-01-01 16:19     ` Daniel Jacobowitz
2007-01-02  6:06       ` [RFA] Add support for catch Ada exceptions (take 2) Joel Brobecker
2007-01-02  6:15         ` Joel Brobecker [this message]
2007-01-03  2:47           ` Daniel Jacobowitz
2007-01-04  5:32             ` Joel Brobecker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20070102061526.GH17211@adacore.com \
    --to=brobecker@adacore.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox