Now that cleanups have been removed, TRY/CATCH can't be SJLJ-based any
more. This patch simplifies the exception handling code, by removing
the non-working variants.
Note that the "pure" C++ exception handling code is removed as well; I
think the route forward must be to change exceptions to be
self-destructing, so that try_scope_depth can simply be removed.
Some longjmp-based code remains, as it is needed to throw an exception
through readline.
gdb/ChangeLog
2019-04-08 Tom Tromey <tom@tromey.com>
* common/common-exceptions.h (GDB_XCPT_SJMP, GDB_XCPT_TRY)
(GDB_XCPT_RAW_TRY, GDB_XCPT): Remove.
(TRY, CATCH, END_CATCH): Remove some definitions.
* common/common-exceptions.c: Don't use GDB_XCPT.
(catcher_list_size): Remove.
(throw_exception, throw_it): Simplify.
This commit applies all changes made after running the gdb/copyright.py
script.
Note that one file was flagged by the script, due to an invalid
copyright header
(gdb/unittests/basic_string_view/element_access/char/empty.cc).
As the file was copied from GCC's libstdc++-v3 testsuite, this commit
leaves this file untouched for the time being; a patch to fix the header
was sent to gcc-patches first.
gdb/ChangeLog:
Update copyright year range in all GDB files.
If you want to use catch_errors with a function with parameters, then
currently you have to manually write a "capture" struct wrapping the
arguments and marshall/unmarshall that.
https://sourceware.org/ml/gdb-patches/2017-09/msg00834.html proposed
adjusting catch_errors to use gdb::function_view, which would allow
passing lambdas with automatic captures. However, it seems like using
TRY/CATCH directly instead ends up producing clearer and easier to
debug code. This is what this commit does.
Note that removing catch_errors exposes further cleanup opportunities
around no longer having to follow catch_errors callback type, and also
removes a few cleanups.
I didn't do anything to save/restore current_uiout because I think
that should be the responsibility of the code that changes
current_uiout in the first place.
(Another approach could be to make catch_errors a variadic template
like:
template<typename Function, typename... Args>
int catch_errors (const char *errstring, return_mask mask,
Function &&func, Args... args);
and then with:
extern void function_with_args (int, int);
extern void function_with_no_args ();
calls to the above functions would be wrapped like this:
catch_errors ("some error happened", RETURN_MASK_ERROR,
function_with_args, arg1, arg2);
catch_errors ("some error happened", RETURN_MASK_ERROR,
function_with_no_args);
but I'm thinking that that doesn't improve much if at all either.)
gdb/ChangeLog
2017-10-10 Pedro Alves <palves@redhat.com>
Tom Tromey <tom@tromey.com>
* breakpoint.c (breakpoint_cond_eval): Change return type to bool
and reverse logic.
(WP_DELETED, WP_VALUE_CHANGED, WP_VALUE_NOT_CHANGED, WP_IGNORE):
No longer macros. Instead ...
(enum wp_check_result): They're now values of this new
enumeration.
(watchpoint_check): Change return type to wp_check_result and
parameter type to bpstat.
(bpstat_check_watchpoint): Use TRY/CATCH instead of catch_errors.
(bpstat_check_breakpoint_conditions): Use TRY/CATCH instead of
catch_errors. Reverse logic of watchpoint_check call.
(breakpoint_re_set_one): Now returns void and takes a breakpoint
pointer as parameter.
(breakpoint_re_set): Use TRY/CATCH instead of catch_errors.
* common/common-exceptions.c (throw_exception_sjlj): Update
comments to avoid mentioning catch_errors.
* exceptions.c (catch_errors): Delete.
* exceptions.h: Update comments to avoid mentioning catch_errors.
(catch_errors_ftype, catch_errors): Delete.
* infrun.c (normal_stop): Use TRY/CATCH instead of catch_errors.
(hook_stop_stub): Delete.
(restore_selected_frame): Change return type to void, and
parameter type to const frame_id &.
(restore_infcall_control_state): Use TRY/CATCH instead of
catch_errors.
* main.c (captured_command_loop): Return void and remove
parameter. Remove references to catch_errors.
(captured_main): Use TRY/CATCH instead of catch_errors.
* objc-lang.c (objc_submethod_helper_data)
(find_objc_msgcall_submethod_helper): Delete.
(find_objc_msgcall_submethod): Use TRY/CATCH instead of
catch_errors.
* record-full.c (record_full_message): Return void.
(record_full_message_args, record_full_message_wrapper): Delete.
(record_full_message_wrapper_safe): Return bool and use TRY/CATCH
instead of catch_errors.
* solib-aix.c (solib_aix_open_symbol_file_object): Change
parameter type to int.
* solib-darwin.c (open_symbol_file_object): Ditto.
* solib-dsbt.c (open_symbol_file_object): Ditto.
* solib-frv.c (open_symbol_file_object): Ditto.
* solib-svr4.c (open_symbol_file_object): Ditto.
* solib-target.c (solib_target_open_symbol_file_object): Ditto.
* solib.c (update_solib_list): Use TRY/CATCH instead of
catch_errors.
* solist.h (struct target_so_ops) <open_symbol_file_object>:
Change type.
* symmisc.c (struct print_symbol_args): Remove.
(dump_symtab_1): Use TRY/CATCH instead of catch_errors.
(print_symbol): Change type.
* windows-nat.c (handle_load_dll, handle_unload_dll): Return void
and remove parameters.
(catch_errors): New.
(get_windows_debug_event): Adjust.
gdb/testsuite/ChangeLog:
2017-10-10 Pedro Alves <palves@redhat.com>
* lib/selftest-support.exp (selftest_setup): Update for
captured_command_loop's prototype change.
This applies the second part of GDB's End of Year Procedure, which
updates the copyright year range in all of GDB's files.
gdb/ChangeLog:
Update copyright year range in all GDB files.
If we map GDB'S TRY/CATCH macros to C++ try/catch, GDB breaks on
systems where readline isn't built with exceptions support. The
problem is that readline calls into GDB through the callback
interface, and if GDB's callback throws a C++ exception/error, the
system unwinder won't manage to unwind past the readline frame, and
ends up calling std::terminate(), which aborts the process:
(gdb) whatever-command-that-causes-an-error
terminate called after throwing an instance of 'gdb_exception_RETURN_MASK_ERROR'
Aborted
$
This went unnoticed for so long because:
- the x86-64 ABI requires -fasynchronous-unwind-tables, making it
possible for exceptions to cross readline with no special handling.
But e.g., on ARM or AIX, unless you build readline with
-fexceptions, you trip on the problem.
- TRY/CATCH was mapped to setjmp/longjmp, even in C++ mode, until
quite recently.
The fix is to catch and save any GDB exception that is thrown inside
the GDB readline callback, and then once the callback returns back to
the GDB code that called into readline in the first place, rethrow the
saved GDB exception.
This is similar in spirit to how we catch/map GDB exceptions at the
GDB/Python and GDB/Guile API boundaries.
The next question is then: if we intercept all exceptions within GDB's
readline callback, should we simply return normally to readline? The
callback prototype has no way to signal an error back to readline (*).
The answer is no -- if we return normally, we'll be returning to a
loop inside rl_callback_read_char that continues processing pending
input, calling into GDB again, redisplaying the prompt, etc. Thus if
we want to error out of rl_callback_read_char, we need to long jump
across it, just like we always did before TRY/CATCH were ever mapped
to C++ exceptions.
My first approach built a specialized API to handle this, with a
couple macros to hide the setjmp/longjmp and the struct gdb_exception
saving/rethrowing.
However, I realized that we need to:
- Handle multiple active rl_callback_read_char invocations. If,
while processing input something triggers a secondary prompt, we
end up in a nested rl_callback_read_char call, through
gdb_readline_wrapper.
- Propagate a struct gdb_exception along with the longjmp.
... and that this is exactly what the setjmp/longjmp-based TRY/CATCH
does.
So the fix makes the setjmp/longjmp TRY/CATCH always available under
new TRY_SJLJ/CATCH_SJLJ aliases, even when TRY/CATCH is mapped to C++
try/catch, and then uses TRY_SJLJ/CATCH_SJLJ to propagate GDB
exceptions across the readline callback.
This turns out to be a much better looking fix than my bespoke API
attempt, even. We'll probably be able to simplify TRY_SJLJ/CATCH_SJLJ
when we finally get rid of TRY/CATCH all over the tree, but until
then, this reuse seems quite nice for avoiding a second parallel
setjmp/longjmp mechanism.
(*) - maybe we could propose a readline API change, but we still need
to handle current readline, anyway.
gdb/ChangeLog:
2016-04-22 Pedro Alves <palves@redhat.com>
* common/common-exceptions.c (enum catcher_state, struct catcher)
(current_catcher): Define in C++ mode too.
(exceptions_state_mc_catch): Call throw_exception_sjlj instead of
throw_exception.
(throw_exception_sjlj, throw_exception_cxx): New functions,
factored out from throw_exception.
(throw_exception): Reimplement.
* common/common-exceptions.h (exceptions_state_mc_init)
(exceptions_state_mc_action_iter)
(exceptions_state_mc_action_iter_1, exceptions_state_mc_catch):
Declare in C++ mode too.
(TRY): Rename to ...
(TRY_SJLJ): ... this.
(CATCH): Rename to ...
(CATCH_SJLJ): ... this.
(END_CATCH): Rename to ...
(END_CATCH_SJLJ): ... this.
[GDB_XCPT == GDB_XCPT_SJMP] (TRY, CATCH, END_CATCH): Map to SJLJ
equivalents.
(throw_exception): Update comments.
(throw_exception_sjlj): Declare.
* event-top.c (gdb_rl_callback_read_char_wrapper): Extend intro
comment. Wrap body in TRY_SJLJ/CATCH_SJLJ and rethrow any
intercepted exception.
(gdb_rl_callback_handler): New function.
(gdb_rl_callback_handler_install): Always install
gdb_rl_callback_handler as readline callback.
Now that we don't ever throw GDB exceptions from signal handlers [1],
we can switch to have TRY/CATCH implemented in terms of plain
setjmp/longjmp instead of sigsetjmp/siglongjmp.
In https://sourceware.org/ml/gdb-patches/2015-02/msg00114.html, Yichun
Zhang mentions a 11%/14%+ speedup in his GDB python scripts with a
patch that did something similar to only a specific set of TRY/CATCH
calls.
[1] - https://sourceware.org/ml/gdb-patches/2016-03/msg00351.html
Tested on x86_64 Fedora 23, native and gdbserver.
gdb/ChangeLog:
2016-04-12 Pedro Alves <palves@redhat.com>
* common/common-exceptions.c (struct catcher) <buf>: Now a
'jmp_buf' instead of SIGJMP_BUF.
(exceptions_state_mc_init): Change return type to 'jmp_buf'.
(throw_exception): Use longjmp instead of SIGLONGJMP.
* common/common-exceptions.h: Include <setjmp.h> instead of
"gdb_setjmp.h".
(exceptions_state_mc_init): Change return type to 'jmp_buf'.
[GDB_XCPT == GDB_XCPT_SJMP] (TRY): Use setjmp instead of
SIGSETJMP.
* cp-support.c: Include "gdb_setjmp.h".
We currently throw exceptions from signal handlers (e.g., for
Quit/ctrl-c). But throwing C++ exceptions from signal handlers is
undefined. (That doesn't restore signal masks, like siglongjmp does,
and, because asynchronous signals can arrive at any instruction, we'd
have to build _everything_ with -fasync-unwind-tables to make it
reliable.) It happens to work on x86_64 GNU/Linux at least, but it's
likely broken on other ports.
Until we stop throwing from signal handlers, use setjmp/longjmp based
exceptions in C++ mode as well.
gdb/ChangeLog:
2015-11-17 Pedro Alves <palves@redhat.com>
* common/common-exceptions.h (GDB_XCPT_SJMP, GDB_XCPT_TRY)
(GDB_XCPT_RAW_TRY, GDB_XCPT): Define.
Replace __cplusplus checks with GDB_XCPT checks throughout.
* common/common-exceptions.c: Replace __cplusplus checks with
GDB_XCPT checks throughout.
Fixes, in C++ mode:
../../src/gdb/common/common-exceptions.c:23:69: error: invalid conversion from ‘int’ to ‘return_reason’ [-fpermissive]
const struct gdb_exception exception_none = { 0, GDB_NO_ERROR, NULL };
^
(I considered adding an enum value for '0', but the code and comments
around return_reason and its uses explain how 0 is special/internal,
so I'm leaving it be.)
gdb/ChangeLog:
2015-10-29 Pedro Alves <palves@redhat.com>
* common/common-exceptions.c (exception_none): Add cast.
Although the current TRY/CATCH implementation works in C++ mode too,
it relies on setjmp/longjmp, and longjmp bypasses calling the
destructors of objects on the stack, which is obviously bad for C++.
This patch fixes this by makes TRY/CATCH use real try/catch in C++
mode behind the scenes. The way this is done allows RAII and cleanups
to coexist while we phase out cleanups, instead of requiring a flag
day.
This patch is not strictly necessary until we require a C++ compiler
and start actually using RAII, though I'm all for baby steps, and it
shows my proposed way forward. Putting it in now, allows for easier
experimentation and exposure of potential problems with real C++
exceptions.
gdb/ChangeLog:
2015-03-07 Pedro Alves <palves@redhat.com>
* common/common-exceptions.c [!__cplusplus] (enum catcher_state)
(exceptions_state_mc_action_iter)
(exceptions_state_mc_action_iter_1, exceptions_state_mc_catch):
Don't define.
[__cplusplus] (try_scope_depth): New global.
[__cplusplus] (exception_try_scope_entry)
(exception_try_scope_exit, gdb_exception_sliced_copy)
(exception_rethrow): New functions.
(throw_exception): In C++ mode, throw
gdb_exception_RETURN_MASK_QUIT for RETURN_QUIT and
gdb_exception_RETURN_MASK_ERROR for RETURN_ERROR.
(throw_it): In C++ mode, use try_scope_depth.
* common/common-exceptions.h [!__cplusplus]
(exceptions_state_mc_action_iter)
(exceptions_state_mc_action_iter_1, exceptions_state_mc_catch):
Don't declare.
[__cplusplus] (exception_try_scope_entry)
(exception_try_scope_exit, exception_rethrow): Declare.
[__cplusplus] (struct exception_try_scope): New struct.
[__cplusplus] (TRY, CATCH, END_CATCH): Reimplement on top of real
C++ exceptions.
(struct gdb_exception_RETURN_MASK_ALL)
(struct gdb_exception_RETURN_MASK_ERROR)
(struct gdb_exception_RETURN_MASK_QUIT): New types.
This patch splits the TRY_CATCH macro into three, so that we go from
this:
~~~
volatile gdb_exception ex;
TRY_CATCH (ex, RETURN_MASK_ERROR)
{
}
if (ex.reason < 0)
{
}
~~~
to this:
~~~
TRY
{
}
CATCH (ex, RETURN_MASK_ERROR)
{
}
END_CATCH
~~~
Thus, we'll be getting rid of the local volatile exception object, and
declaring the caught exception in the catch block.
This allows reimplementing TRY/CATCH in terms of C++ exceptions when
building in C++ mode, while still allowing to build GDB in C mode
(using setjmp/longjmp), as a transition step.
TBC, after this patch, is it _not_ valid to have code between the TRY
and the CATCH blocks, like:
TRY
{
}
// some code here.
CATCH (ex, RETURN_MASK_ERROR)
{
}
END_CATCH
Just like it isn't valid to do that with C++'s native try/catch.
By switching to creating the exception object inside the CATCH block
scope, we can get rid of all the explicitly allocated volatile
exception objects all over the tree, and map the CATCH block more
directly to C++'s catch blocks.
The majority of the TRY_CATCH -> TRY+CATCH+END_CATCH conversion was
done with a script, rerun from scratch at every rebase, no manual
editing involved. After the mechanical conversion, a few places
needed manual intervention, to fix preexisting cases where we were
using the exception object outside of the TRY_CATCH block, and cases
where we were using "else" after a 'if (ex.reason) < 0)' [a CATCH
after this patch]. The result was folded into this patch so that GDB
still builds at each incremental step.
END_CATCH is necessary for two reasons:
First, because we name the exception object in the CATCH block, which
requires creating a scope, which in turn must be closed somewhere.
Declaring the exception variable in the initializer field of a for
block, like:
#define CATCH(EXCEPTION, mask) \
for (struct gdb_exception EXCEPTION; \
exceptions_state_mc_catch (&EXCEPTION, MASK); \
EXCEPTION = exception_none)
would avoid needing END_CATCH, but alas, in C mode, we build with C90,
which doesn't allow mixed declarations and code.
Second, because when TRY/CATCH are wired to real C++ try/catch, as
long as we need to handle cleanup chains, even if there's no CATCH
block that wants to catch the exception, we need for stop at every
frame in the unwind chain and run cleanups, then rethrow. That will
be done in END_CATCH.
After we require C++, we'll still need TRY/CATCH/END_CATCH until
cleanups are completely phased out -- TRY/CATCH in C++ mode will
save/restore the current cleanup chain, like in C mode, and END_CATCH
catches otherwise uncaugh exceptions, runs cleanups and rethrows, so
that C++ cleanups and exceptions can coexist.
IMO, this still makes the TRY/CATCH code look a bit more like a
newcomer would expect, so IMO worth it even if we weren't considering
C++.
gdb/ChangeLog.
2015-03-07 Pedro Alves <palves@redhat.com>
* common/common-exceptions.c (struct catcher) <exception>: No
longer a pointer to volatile exception. Now an exception value.
<mask>: Delete field.
(exceptions_state_mc_init): Remove all parameters. Adjust.
(exceptions_state_mc): No longer pop the catcher here.
(exceptions_state_mc_catch): New function.
(throw_exception): Adjust.
* common/common-exceptions.h (exceptions_state_mc_init): Remove
all parameters.
(exceptions_state_mc_catch): Declare.
(TRY_CATCH): Rename to ...
(TRY): ... this. Remove EXCEPTION and MASK parameters.
(CATCH, END_CATCH): New.
All callers adjusted.
gdb/gdbserver/ChangeLog:
2015-03-07 Pedro Alves <palves@redhat.com>
Adjust all callers of TRY_CATCH to use TRY/CATCH/END_CATCH
instead.
gdb/ChangeLog:
2015-02-27 Pedro Alves <palves@redhat.com>
* common/common-exceptions.h (exception_none): Declare.
* common/common-exceptions.c (exception_none): Moved from
exceptions.c.
(exceptions_state_mc_init): Use exception_none.
* exceptions.c (exception_none): Move to
common/common-exceptions.c.
* exceptions.h (exception_none): Move to
common/common-exceptions.h.
This commit includes cleanups.h in common-defs.h and removes all other
inclusions.
gdb/ChangeLog:
* common/common-defs.h: Include cleanups.h.
* common/common-exceptions.c: Do not include cleanups.h.
* utils.h: Likewise.
gdb/gdbserver/ChangeLog:
* server.h: Do not include cleanups.h.