This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
[rfc/rfa(top.c)] catch_exceptions()
- To: gdb-patches at sources dot redhat dot com
- Subject: [rfc/rfa(top.c)] catch_exceptions()
- From: Andrew Cagney <ac131313 at cygnus dot com>
- Date: Mon, 13 Aug 2001 16:42:49 -0400
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