This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

[rfc/rfa(top.c)] catch_exceptions()


Hello,

The attatched patch impements a successor to catch_errors() - 
catch_exceptions().  If you read all the FIXMEs  (appended), two 
alternatives are suggested:

>  When returning normally, i.e. without catching an error or a
> quit, catch_event() could return RETURN_NORMAL, which would be
> added to enum return_reason. FUNC would return information
> exclusively via ARGS.
> 
> Alternatively, normal catch_event() could return FUNC's return
> value. The caller would need to be aware of potential overlap
> with enum return_reason, which could be publicly restricted to
> negative values to simplify return value processing in FUNC and
> in the caller. */ [nsd]


I implemented the second.  For strictly arbitrary reasons.  I really 
don't remember which of the above was my preference when Nick added the 
above comment.

The new function:

	o	handles UIOUT
		(catch errors didn't)

	o	returns a catch indication
		(catch errors didn't)

	o	checks that the wrapped FUNC()
		compiles to certain basic
		requirements.
		(catch errors didn't)

I should note that the final version of this function depends on what 
happens to my previous patch.

The successor to this patch is to gdb.h where I'll add that missing 
uiout parameter to all the gdb client calls, after that is one to allow 
the MI to query the stop reason .....

Comments.  Any preference for the other.  Ok for top.c?

	Andrew

Ref: top.c

/* Call FUNC with arg ARGS, catching any errors.  If there is no
    error, return the value returned by FUNC.  If there is an error,
    print ERRSTRING, print the specific error message, then return
    zero.

    Must not be called with immediate_quit in effect (bad things might
    happen, say we got a signal in the middle of a memcpy to quit_return).
    This is an OK restriction; with very few exceptions immediate_quit can
    be replaced by judicious use of QUIT.

    MASK specifies what to catch; it is normally set to
    RETURN_MASK_ALL, if for no other reason than that the code which
    calls catch_errors might not be set up to deal with a quit which
    isn't caught.  But if the code can deal with it, it generally
    should be RETURN_MASK_ERROR, unless for some reason it is more
    useful to abort only the portion of the operation inside the
    catch_errors.  Note that quit should return to the command line
    fairly quickly, even if some further processing is being done.  */

/* MAYBE: cagney/1999-11-05: catch_errors() in conjunction with
    error() et.al. could maintain a set of flags that indicate the the
    current state of each of the longjmp buffers.  This would give the
    longjmp code the chance to detect a longjmp botch (before it gets
    to longjmperror()).  Prior to 1999-11-05 this wasn't possible as
    code also randomly used a SET_TOP_LEVEL macro that directly
    initialize the longjmp buffers. */

/* MAYBE: cagney/1999-11-05: Should the catch_errors and cleanups code
    be consolidated into a single file instead of being distributed
    between utils.c and top.c? */


   /* FIXME: cagney/1999-11-05: A correct FUNC implementation will
      clean things up (restoring the cleanup chain) to the state they
      were just prior to the call.  Unfortunately, many FUNC's are not
      that well behaved.  This could be fixed by adding either a
      do_cleanups call (to cover the problem) or an assertion check to
      detect bad FUNCs code. */

   /* Tell the caller that an event was caught.

      FIXME: nsd/2000-02-22: When MASK is RETURN_MASK_ALL, the caller
      can't tell what type of event occurred.

      A possible fix is to add a new interface, catch_event(), that
      returns enum return_reason after catching an error or a quit.

      When returning normally, i.e. without catching an error or a
      quit, catch_event() could return RETURN_NORMAL, which would be
      added to enum return_reason.  FUNC would return information
      exclusively via ARGS.

      Alternatively, normal catch_event() could return FUNC's return
      value.  The caller would need to be aware of potential overlap
      with enum return_reason, which could be publicly restricted to
      negative values to simplify return value processing in FUNC and
      in the caller. */

   /* FIXME: cagney/1999-11-07: Technically this do_cleanups() call
      isn't needed.  Instead an assertion check could be made that
      simply confirmed that the called function correctly cleaned up
      after itself.  Unfortunately, old code (prior to 1999-11-04) in
      main.c was calling SET_TOP_LEVEL(), calling the command function,
      and then *always* calling do_cleanups().  For the moment we
      remain ``bug compatible'' with that old code..  */
   do_cleanups (ALL_CLEANUPS);
2001-08-13  Andrew Cagney  <ac131313@redhat.com>

	* defs.h (enum return_reason): Renumber so that all values are
	negative.
	(RETURN_MASK_QUIT, RETURN_MASK_ERROR): Negate negative enum value.
	(catch_exception_ftype): Declare.
 	(catch_exceptions): Declare.
	* top.c (catcher): New function, based on catch_errors.  Add in
	parameter func_uiout and out parameters func_val, func_caught and
	func_cleanup.  Change type of func to catch_exceptions_ftype.
	Save/restore uiout.
	(struct catch_errors_args): Define.
	(do_catch_errors): New function.
	(catch_errors): Rewrite, use do_catch_errors and catcher.
	(catch_exceptions): New function, use catcher.

Index: defs.h
===================================================================
RCS file: /cvs/src/src/gdb/defs.h,v
retrieving revision 1.62
diff -p -r1.62 defs.h
*** defs.h	2001/08/02 20:57:19	1.62
--- defs.h	2001/08/13 19:53:18
*************** extern NORETURN void nomem (long) ATTR_N
*** 1091,1117 ****
  enum return_reason
    {
      /* User interrupt.  */
!     RETURN_QUIT = 1,
      /* Any other error.  */
      RETURN_ERROR
    };
  
! #define	ALL_CLEANUPS	((struct cleanup *)0)
  
  #define RETURN_MASK(reason)	(1 << (int)(reason))
! #define RETURN_MASK_QUIT	RETURN_MASK (RETURN_QUIT)
! #define RETURN_MASK_ERROR	RETURN_MASK (RETURN_ERROR)
  #define RETURN_MASK_ALL		(RETURN_MASK_QUIT | RETURN_MASK_ERROR)
  typedef int return_mask;
  
  extern NORETURN void return_to_top_level (enum return_reason) ATTR_NORETURN;
  
  /* If CATCH_ERRORS_FTYPE throws an error, catch_errors() returns zero
     otherwize the result from CATCH_ERRORS_FTYPE is returned. It is
     probably useful for CATCH_ERRORS_FTYPE to always return a non-zero
     value. It's unfortunate that, catch_errors() does not return an
     indication of the exact exception that it caught - quit_flag might
!    help. */
  
  typedef int (catch_errors_ftype) (PTR);
  extern int catch_errors (catch_errors_ftype *, PTR, char *, return_mask);
--- 1092,1147 ----
  enum return_reason
    {
      /* User interrupt.  */
!     RETURN_QUIT = -2,
      /* Any other error.  */
      RETURN_ERROR
    };
  
! #define	ALL_CLEANUPS	((struct cleanup *)0) 
  
  #define RETURN_MASK(reason)	(1 << (int)(reason))
! #define RETURN_MASK_QUIT	RETURN_MASK (-RETURN_QUIT)
! #define RETURN_MASK_ERROR	RETURN_MASK (-RETURN_ERROR)
  #define RETURN_MASK_ALL		(RETURN_MASK_QUIT | RETURN_MASK_ERROR)
  typedef int return_mask;
  
  extern NORETURN void return_to_top_level (enum return_reason) ATTR_NORETURN;
  
+ /* Call FUNC(UIOUT, FUNC_ARGS) but wrapped within an exception
+    handler.  If an exception (enum return_reason) is thrown using
+    return_to_top_level() than all cleanups installed since
+    catch_exceptions() was entered are invoked, the (-ve) exception
+    value is then returned by catch_exceptions.  If FUNC() returns
+    normally (with a postive or zero return value) then that value is
+    returned by catch_exceptions().  It is an internal_error() for
+    FUNC() to return a negative value.
+ 
+    For the period of the FUNC() call: UIOUT is installed as the output
+    builder; ERRSTRING is installed as the error/quit message; and a
+    new cleanup_chain is established.  The old values are restored
+    before catch_exceptions() returns.
+ 
+    FIXME; cagney/2001-08-13: The need to override the global UIOUT
+    builder variable should just go away.
+ 
+    This function superseeds catch_errors().
+ 
+    This function uses SETJMP() and LONGJUMP().  */
+ 
+ struct ui_out;
+ typedef int (catch_exceptions_ftype) (struct ui_out *ui_out, void *args);
+ extern int catch_exceptions (struct ui_out *uiout,
+ 			     catch_exceptions_ftype *func, void *func_args,
+ 			     char *errstring, return_mask mask);
+ 
  /* If CATCH_ERRORS_FTYPE throws an error, catch_errors() returns zero
     otherwize the result from CATCH_ERRORS_FTYPE is returned. It is
     probably useful for CATCH_ERRORS_FTYPE to always return a non-zero
     value. It's unfortunate that, catch_errors() does not return an
     indication of the exact exception that it caught - quit_flag might
!    help.
! 
!    This function is superseeded by catch_exceptions().  */
  
  typedef int (catch_errors_ftype) (PTR);
  extern int catch_errors (catch_errors_ftype *, PTR, char *, return_mask);
Index: top.c
===================================================================
RCS file: /cvs/src/src/gdb/top.c,v
retrieving revision 1.42
diff -p -r1.42 top.c
*** top.c	2001/08/01 18:39:23	1.42
--- top.c	2001/08/13 19:53:18
***************
*** 41,46 ****
--- 41,47 ----
  #include "version.h"
  #include "serial.h"
  #include "doublest.h"
+ #include "gdb_assert.h"
  
  /* readline include files */
  #include <readline/readline.h>
*************** return_to_top_level (enum return_reason 
*** 378,472 ****
     be consolidated into a single file instead of being distributed
     between utils.c and top.c? */
  
! int
! catch_errors (catch_errors_ftype *func, void * args, char *errstring,
! 	      return_mask mask)
  {
    SIGJMP_BUF *saved_catch;
    SIGJMP_BUF catch;
-   int val;
    struct cleanup *saved_cleanup_chain;
    char *saved_error_pre_print;
    char *saved_quit_pre_print;
  
    /* Return value from SIGSETJMP(): enum return_reason if error or
       quit caught, 0 otherwise. */
    int caught;
  
!   /* Override error/quit messages during FUNC. */
  
    saved_error_pre_print = error_pre_print;
    saved_quit_pre_print = quit_pre_print;
- 
    if (mask & RETURN_MASK_ERROR)
      error_pre_print = errstring;
    if (mask & RETURN_MASK_QUIT)
      quit_pre_print = errstring;
  
    /* Prevent error/quit during FUNC from calling cleanups established
       prior to here. */
  
    saved_cleanup_chain = save_cleanups ();
  
    /* Call FUNC, catching error/quit events. */
- 
    saved_catch = catch_return;
    catch_return = &catch;
    caught = SIGSETJMP (catch);
    if (!caught)
!     val = (*func) (args);
    else
      val = 0;
    catch_return = saved_catch;
  
!   /* FIXME: cagney/1999-11-05: A correct FUNC implementation will
!      clean things up (restoring the cleanup chain) to the state they
!      were just prior to the call.  Unfortunately, many FUNC's are not
!      that well behaved.  This could be fixed by adding either a
!      do_cleanups call (to cover the problem) or an assertion check to
!      detect bad FUNCs code. */
  
!   /* Restore the cleanup chain and error/quit messages to their
!      original states. */
  
!   restore_cleanups (saved_cleanup_chain);
  
!   if (mask & RETURN_MASK_QUIT)
!     quit_pre_print = saved_quit_pre_print;
!   if (mask & RETURN_MASK_ERROR)
!     error_pre_print = saved_error_pre_print;
  
!   /* Return normally if no error/quit event occurred. */
  
!   if (!caught)
!     return val;
  
!   /* If the caller didn't request that the event be caught, relay the
!      event to the next containing catch_errors(). */
  
!   if (!(mask & RETURN_MASK (caught)))
!     return_to_top_level (caught);
  
!   /* Tell the caller that an event was caught.
  
!      FIXME: nsd/2000-02-22: When MASK is RETURN_MASK_ALL, the caller
!      can't tell what type of event occurred.
  
!      A possible fix is to add a new interface, catch_event(), that
!      returns enum return_reason after catching an error or a quit.
  
!      When returning normally, i.e. without catching an error or a
!      quit, catch_event() could return RETURN_NORMAL, which would be
!      added to enum return_reason.  FUNC would return information
!      exclusively via ARGS.
  
!      Alternatively, normal catch_event() could return FUNC's return
!      value.  The caller would need to be aware of potential overlap
!      with enum return_reason, which could be publicly restricted to
!      negative values to simplify return value processing in FUNC and
!      in the caller. */
  
!   return 0;
  }
  
  struct captured_command_args
--- 379,521 ----
     be consolidated into a single file instead of being distributed
     between utils.c and top.c? */
  
! /* Call FUNC() as per catch_exceptions().  Return the final state in
!    FUNC_VAL, FUNC_CAUGHT and FUNC_CLEANUP where it can be analyzed by
!    the caller.  */
! 
! static void
! catcher (catch_exceptions_ftype *func,
! 	 struct ui_out *func_uiout,
! 	 void *func_args,
! 	 int *func_val,
! 	 enum return_reason *func_caught,
! 	 struct cleanup **func_cleanups,
! 	 char *errstring,
! 	 return_mask mask)
  {
    SIGJMP_BUF *saved_catch;
    SIGJMP_BUF catch;
    struct cleanup *saved_cleanup_chain;
    char *saved_error_pre_print;
    char *saved_quit_pre_print;
+   struct ui_out *saved_uiout;
  
    /* Return value from SIGSETJMP(): enum return_reason if error or
       quit caught, 0 otherwise. */
    int caught;
  
!   /* Return value from FUNC(): Hopefully non-zero. Explicitly set to
!      zero if an error quit was caught.  */
!   int val;
  
+   /* Override error/quit messages, and the UI_OUT builder during
+      FUNC().  Don't try to be smart, alwas save the old value.  */
+ 
    saved_error_pre_print = error_pre_print;
    saved_quit_pre_print = quit_pre_print;
    if (mask & RETURN_MASK_ERROR)
      error_pre_print = errstring;
    if (mask & RETURN_MASK_QUIT)
      quit_pre_print = errstring;
  
+   saved_uiout = uiout;
+   uiout = func_uiout;
+ 
    /* Prevent error/quit during FUNC from calling cleanups established
       prior to here. */
  
    saved_cleanup_chain = save_cleanups ();
  
    /* Call FUNC, catching error/quit events. */
    saved_catch = catch_return;
    catch_return = &catch;
    caught = SIGSETJMP (catch);
    if (!caught)
!     val = (*func) (func_uiout, func_args);
    else
      val = 0;
    catch_return = saved_catch;
  
!   *func_val = val;
!   *func_caught = caught;
  
!   /* Restore the cleanup chain to its original state.  NOTE:
!      cagney/1999-11-05: A correct FUNC implementation will clean
!      things up (restoring the cleanup chain) to the state it was just
!      prior to the call.  Unfortunately, many FUNC's are not that well
!      behaved.  The old cleanup chan is returned via func_cleanups so
!      that the caller can, in the case of catch_exceptions() verify
!      correct behavour.  */
  
!   *func_cleanups = restore_cleanups (saved_cleanup_chain);
  
!   /* Restore the error/quit messages and the uiout builder to their
!      original states.  */
  
!   uiout = saved_uiout;
  
!   quit_pre_print = saved_quit_pre_print;
!   error_pre_print = saved_error_pre_print;
  
!   /* Return normally if no error/quit event occurred or this catcher
!      can handle this exception.  The caller analyses the func return
!      values.  */
  
!   if (!caught || (mask & RETURN_MASK (caught)))
!     return;
  
!   /* The caller didn't request that the event be caught, relay the
!      event to the next containing catch_errors(). */
  
!   return_to_top_level (caught);
! }
  
! int
! catch_exceptions (struct ui_out *uiout,
! 		  catch_exceptions_ftype *func,
! 		  void *func_args,
! 		  char *errstring,
! 		  return_mask mask)
! {
!   int val;
!   enum return_reason caught;
!   struct cleanup *cleanups;
!   catcher (func, uiout, func_args, &val, &caught, &cleanups, errstring, mask);
!   gdb_assert (val >= 0);
!   gdb_assert (caught <= 0);
!   if (caught < 0)
!     return caught;
!   return val;
! }
  
! struct catch_errors_args
! {
!   catch_errors_ftype *func;
!   void *func_args;
! };
  
! int
! do_catch_errors (struct ui_out *uiout, void *data)
! {
!   struct catch_errors_args *args = data;
!   return args->func (args->func_args);
! }
  
! int
! catch_errors (catch_errors_ftype *func, void *func_args, char *errstring,
! 	      return_mask mask)
! {
!   int val;
!   enum return_reason caught;
!   struct catch_errors_args args;
!   struct cleanup *cleanups;
!   args.func = func;
!   args.func_args = func_args;
!   catcher (do_catch_errors, uiout, &args, &val, &caught, &cleanups,
! 	   errstring, mask);
!   if (caught != 0)
!     return 0;
!   return val;
  }
  
  struct captured_command_args

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]