gdb/gdb-next-over-throw.patch

1284 lines
37 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

http://sourceware.org/ml/gdb-patches/2010-10/msg00109.html
Subject: RFC: next/finish/etc -vs- exceptions
This is a refresh of:
http://www.sourceware.org/ml/gdb-patches/2009-05/msg00635.html
(The thread continues in the following month.)
I rebased the patch and addressed Pedro's comments.
I did not add the user notification that Doug asked for.
This version does work if an exception is thrown from a signal handler.
The included Java test case checks that (on most platforms).
Built and regtested on x86-64 (compile farm).
Tests for the new functionality are included.
I'd appreciate comments. I don't want to commit this without a review,
as my experience is that I usually err when touching breakpoints or
infrun.
If you want to try this yourself, you will need a new-enough libgcc, one
that includes the necessary debugging hook.
Tom
2010-10-06 Tom Tromey <tromey@redhat.com>
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(handle_inferior_event): Likewise.
(insert_exception_resume_breakpoint): New function.
(check_exception_resume): Likewise.
* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
(step_1): Set thread's initiating frame.
(until_next_continuation): New function.
(until_next_command): Support exception breakpoints.
(finish_command_continuation): Delete longjmp breakpoint.
(finish_forward): Support exception breakpoints.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
bp_exception_master>: New constants.
(struct bpstat_what) <is_longjmp>: New field.
* breakpoint.c (create_exception_master_breakpoint): New function.
(update_breakpoints_after_exec): Handle bp_exception_master. Call
create_exception_master_breakpoint.
(print_it_typical): Handle bp_exception_master, bp_exception.
(bpstat_stop_status): Handle bp_exception_master.
(bpstat_what): Handle bp_exception_master, bp_exception,
bp_exception_resume.
(bptype_string): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(set_longjmp_breakpoint): Handle exception breakpoints.
(delete_longjmp_breakpoint): Likewise.
(mention): Likewise.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Support exception breakpoints.
(delete_command): Likewise.
(breakpoint_re_set_one): Likewise.
(breakpoint_re_set): Likewise.
2010-10-06 Tom Tromey <tromey@redhat.com>
* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* gdb.cp/gdb9593.exp: New file.
* gdb.cp/gdb9593.cc: New file.
Index: gdb-7.2.50.20101116/gdb/breakpoint.c
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/breakpoint.c 2010-11-17 02:38:59.000000000 +0100
+++ gdb-7.2.50.20101116/gdb/breakpoint.c 2010-11-17 02:41:53.000000000 +0100
@@ -2232,6 +2232,33 @@ create_std_terminate_master_breakpoint (
do_cleanups (old_chain);
}
+/* Install a master breakpoint on the unwinder's debug hook. */
+
+void
+create_exception_master_breakpoint (void)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *debug_hook;
+
+ debug_hook = lookup_minimal_symbol_text ("_Unwind_DebugHook", objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (get_objfile_arch (objfile),
+ SYMBOL_VALUE_ADDRESS (debug_hook),
+ bp_exception_master);
+ b->addr_string = xstrdup ("_Unwind_DebugHook");
+ b->enable_state = bp_disabled;
+ }
+ }
+
+ update_global_location_list (1);
+}
+
void
update_breakpoints_after_exec (void)
{
@@ -2273,7 +2300,8 @@ update_breakpoints_after_exec (void)
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master || b->type == bp_std_terminate_master)
+ || b->type == bp_longjmp_master || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
@@ -2288,7 +2316,8 @@ update_breakpoints_after_exec (void)
/* Longjmp and longjmp-resume breakpoints are also meaningless
after an exec. */
- if (b->type == bp_longjmp || b->type == bp_longjmp_resume)
+ if (b->type == bp_longjmp || b->type == bp_longjmp_resume
+ || b->type == bp_exception || b->type == bp_exception_resume)
{
delete_breakpoint (b);
continue;
@@ -2350,6 +2379,7 @@ update_breakpoints_after_exec (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
int
@@ -3275,6 +3305,12 @@ print_it_typical (bpstat bs)
result = PRINT_NOTHING;
break;
+ case bp_exception_master:
+ /* These should never be enabled. */
+ printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
+ result = PRINT_NOTHING;
+ break;
+
case bp_watchpoint:
case bp_hardware_watchpoint:
annotate_watchpoint (b->number);
@@ -3362,6 +3398,8 @@ print_it_typical (bpstat bs)
case bp_none:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4164,7 +4202,8 @@ bpstat_stop_status (struct address_space
if (b->type == bp_thread_event || b->type == bp_overlay_event
|| b->type == bp_longjmp_master
- || b->type == bp_std_terminate_master)
+ || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
@@ -4260,6 +4299,7 @@ bpstat_what (bpstat bs_head)
retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;
for (bs = bs_head; bs != NULL; bs = bs->next)
{
@@ -4315,10 +4355,14 @@ bpstat_what (bpstat bs_head)
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -4334,6 +4378,7 @@ bpstat_what (bpstat bs_head)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
this_action = BPSTAT_WHAT_SINGLE;
break;
case bp_catchpoint:
@@ -4556,6 +4601,8 @@ bptype_string (enum bptype type)
{bp_access_watchpoint, "acc watchpoint"},
{bp_longjmp, "longjmp"},
{bp_longjmp_resume, "longjmp resume"},
+ {bp_exception, "exception"},
+ {bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
@@ -4565,6 +4612,7 @@ bptype_string (enum bptype type)
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
{bp_std_terminate_master, "std::terminate master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
{bp_fast_tracepoint, "fast tracepoint"},
@@ -4705,6 +4753,8 @@ print_one_breakpoint_location (struct br
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4714,6 +4764,7 @@ print_one_breakpoint_location (struct br
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
case bp_tracepoint:
case bp_fast_tracepoint:
case bp_static_tracepoint:
@@ -5457,6 +5508,8 @@ allocate_bp_location (struct breakpoint
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -5469,6 +5522,7 @@ allocate_bp_location (struct breakpoint
case bp_std_terminate_master:
case bp_gnu_ifunc_resolver:
case bp_gnu_ifunc_resolver_return:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -5708,8 +5762,7 @@ make_breakpoint_permanent (struct breakp
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in THREAD. */
void
set_longjmp_breakpoint (int thread)
@@ -5722,11 +5775,12 @@ set_longjmp_breakpoint (int thread)
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
if (b->pspace == current_program_space
- && b->type == bp_longjmp_master)
+ && (b->type == bp_longjmp_master
+ || b->type == bp_exception_master))
{
struct breakpoint *clone = clone_momentary_breakpoint (b);
- clone->type = bp_longjmp;
+ clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
clone->thread = thread;
}
}
@@ -5738,7 +5792,7 @@ delete_longjmp_breakpoint (int thread)
struct breakpoint *b, *temp;
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp)
+ if (b->type == bp_longjmp || b->type == bp_exception)
{
if (b->thread == thread)
delete_breakpoint (b);
@@ -6913,6 +6967,8 @@ mention (struct breakpoint *b)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
@@ -6924,6 +6980,7 @@ mention (struct breakpoint *b)
case bp_longjmp_master:
case bp_std_terminate_master:
case bp_gnu_ifunc_resolver_return:
+ case bp_exception_master:
break;
}
@@ -8598,6 +8655,7 @@ struct until_break_command_continuation_
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};
/* This function is called by fetch_inferior_event via the
@@ -8612,6 +8670,7 @@ until_break_command_continuation (void *
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}
void
@@ -8623,6 +8682,8 @@ until_break_command (char *arg, int from
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;
clear_proceed_status ();
@@ -8661,6 +8722,9 @@ until_break_command (char *arg, int from
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ tp = inferior_thread ();
+ thread = tp->num;
+
/* Keep within the current frame, or in frames called by the current
one. */
@@ -8673,6 +8737,10 @@ until_break_command (char *arg, int from
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = frame_unwind_caller_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8689,6 +8757,7 @@ until_break_command (char *arg, int from
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
@@ -9940,6 +10009,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
@@ -9961,6 +10031,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
@@ -10470,6 +10541,7 @@ breakpoint_re_set_one (void *bint)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
@@ -10493,6 +10565,8 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_jit_event:
case bp_gnu_ifunc_resolver_return:
break;
@@ -10537,6 +10611,7 @@ breakpoint_re_set (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
/* Reset the thread number of this breakpoint:
Index: gdb-7.2.50.20101116/gdb/breakpoint.h
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/breakpoint.h 2010-11-17 02:38:59.000000000 +0100
+++ gdb-7.2.50.20101116/gdb/breakpoint.h 2010-11-17 02:39:19.000000000 +0100
@@ -61,6 +61,13 @@ enum bptype
bp_longjmp, /* secret breakpoint to find longjmp() */
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
+ /* An internal breakpoint that is installed on the unwinder's
+ debug hook. */
+ bp_exception,
+ /* An internal breakpoint that is set at the point where an
+ exception will land. */
+ bp_exception_resume,
+
/* Used by wait_for_inferior for stepping over subroutine calls, for
stepping over signal handlers, and for skipping prologues. */
bp_step_resume,
@@ -130,6 +137,9 @@ enum bptype
/* Master copies of std::terminate breakpoints. */
bp_std_terminate_master,
+ /* Like bp_longjmp_master, but for exceptions. */
+ bp_exception_master,
+
bp_catchpoint,
bp_tracepoint,
@@ -672,6 +682,10 @@ struct bpstat_what
continuing from a call dummy without popping the frame is not a
useful one). */
enum stop_stack_kind call_dummy;
+
+ /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME. True if we are
+ handling a longjmp, false if we are handling an exception. */
+ int is_longjmp;
};
/* The possible return values for print_bpstat, print_it_normal,
Index: gdb-7.2.50.20101116/gdb/gdbthread.h
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/gdbthread.h 2010-11-02 02:37:31.000000000 +0100
+++ gdb-7.2.50.20101116/gdb/gdbthread.h 2010-11-17 02:39:19.000000000 +0100
@@ -185,6 +185,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
Index: gdb-7.2.50.20101116/gdb/infcmd.c
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/infcmd.c 2010-07-01 17:36:15.000000000 +0200
+++ gdb-7.2.50.20101116/gdb/infcmd.c 2010-11-17 02:39:19.000000000 +0100
@@ -822,7 +822,7 @@ nexti_command (char *count_string, int f
step_1 (1, 1, count_string);
}
-static void
+void
delete_longjmp_breakpoint_cleanup (void *arg)
{
int thread = * (int *) arg;
@@ -862,10 +862,13 @@ step_1 (int skip_subroutines, int single
if (!single_inst || skip_subroutines) /* leave si command alone */
{
+ struct thread_info *tp = inferior_thread ();
+
if (in_thread_list (inferior_ptid))
thread = pid_to_thread_id (inferior_ptid);
set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (get_current_frame ());
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1219,6 +1222,15 @@ signal_command (char *signum_exp, int fr
proceed ((CORE_ADDR) -1, oursig, 0);
}
+/* A continuation callback for until_next_command. */
+
+static void
+until_next_continuation (void *arg)
+{
+ struct thread_info *tp = arg;
+ delete_longjmp_breakpoint (tp->num);
+}
+
/* Proceed until we reach a different source line with pc greater than
our current one or exit the function. We skip calls in both cases.
@@ -1235,6 +1247,8 @@ until_next_command (int from_tty)
struct symbol *func;
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
+ int thread = tp->num;
+ struct cleanup *old_chain;
clear_proceed_status ();
set_step_frame ();
@@ -1270,7 +1284,19 @@ until_next_command (int from_tty)
tp->step_multi = 0; /* Only one call to proceed */
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (frame);
+ old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+ if (target_can_async_p () && is_running (inferior_ptid))
+ {
+ discard_cleanups (old_chain);
+ add_continuation (tp, until_next_continuation, tp, NULL);
+ }
+ else
+ do_cleanups (old_chain);
}
static void
@@ -1463,6 +1489,7 @@ finish_command_continuation (void *arg)
if (bs != NULL && tp->proceed_to_finish)
observer_notify_normal_stop (bs, 1 /* print frame */);
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}
static void
@@ -1546,6 +1573,7 @@ finish_forward (struct symbol *function,
struct breakpoint *breakpoint;
struct cleanup *old_chain;
struct finish_command_continuation_args *cargs;
+ int thread = tp->num;
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
@@ -1556,6 +1584,10 @@ finish_forward (struct symbol *function,
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
cargs = xmalloc (sizeof (*cargs));
Index: gdb-7.2.50.20101116/gdb/inferior.h
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/inferior.h 2010-11-17 02:38:55.000000000 +0100
+++ gdb-7.2.50.20101116/gdb/inferior.h 2010-11-17 02:39:19.000000000 +0100
@@ -299,6 +299,8 @@ extern void interrupt_target_command (ch
extern void interrupt_target_1 (int all_threads);
+extern void delete_longjmp_breakpoint_cleanup (void *arg);
+
extern void detach_command (char *, int);
extern void notice_new_inferior (ptid_t, int, int);
Index: gdb-7.2.50.20101116/gdb/infrun.c
===================================================================
--- gdb-7.2.50.20101116.orig/gdb/infrun.c 2010-11-17 02:38:59.000000000 +0100
+++ gdb-7.2.50.20101116/gdb/infrun.c 2010-11-17 02:39:19.000000000 +0100
@@ -45,6 +45,8 @@
#include "language.h"
#include "solib.h"
#include "main.h"
+#include "dictionary.h"
+#include "block.h"
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
@@ -2229,6 +2231,8 @@ static void insert_step_resume_breakpoin
struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void check_exception_resume (struct execution_control_state *,
+ struct frame_info *, struct symbol *);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
@@ -4121,23 +4125,33 @@ process_event_stop_test:
ecs->event_thread->stepping_over_breakpoint = 1;
- if (!gdbarch_get_longjmp_target_p (gdbarch)
- || !gdbarch_get_longjmp_target (gdbarch, frame, &jmp_buf_pc))
+ if (what.is_longjmp)
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
+ if (!gdbarch_get_longjmp_target_p (gdbarch)
+ || !gdbarch_get_longjmp_target (gdbarch,
+ frame, &jmp_buf_pc))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "\
infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
- keep_going (ecs);
- return;
- }
+ keep_going (ecs);
+ return;
+ }
- /* We're going to replace the current step-resume breakpoint
- with a longjmp-resume breakpoint. */
- delete_step_resume_breakpoint (ecs->event_thread);
+ /* We're going to replace the current step-resume breakpoint
+ with a longjmp-resume breakpoint. */
+ delete_step_resume_breakpoint (ecs->event_thread);
- /* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ /* Insert a breakpoint at resume address. */
+ insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ }
+ else
+ {
+ struct symbol *func = get_frame_function (frame);
+ if (func)
+ check_exception_resume (ecs, frame, func);
+ }
keep_going (ecs);
return;
@@ -4149,6 +4163,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (
gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
delete_step_resume_breakpoint (ecs->event_thread);
+ if (!what.is_longjmp)
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame.
+
+ 2.1. If we are stepping, defer to the stepping logic.
+
+ 2.2. Otherwise, we are not stepping, so we are doing a
+ "finish" and we have reached the calling frame. So,
+ stop.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ if (ecs->event_thread->step_range_start)
+ {
+ /* Case 2.1. */
+ break;
+ }
+ else
+ {
+ /* Case 2.2: fall through. */
+ }
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+ }
+
ecs->event_thread->stop_step = 1;
print_end_stepping_range_reason ();
stop_stepping (ecs);
@@ -5127,6 +5188,100 @@ insert_longjmp_resume_breakpoint (struct
set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
+/* Insert an exception resume breakpoint. TP is the thread throwing
+ the exception. The block B is the block of the unwinder debug hook
+ function. FRAME is the frame corresponding to the call to this
+ function. SYM is the symbol of the function argument holding the
+ target PC of the exception. */
+
+static void
+insert_exception_resume_breakpoint (struct thread_info *tp,
+ struct block *b,
+ struct frame_info *frame,
+ struct symbol *sym)
+{
+ struct gdb_exception e;
+
+ /* We want to ignore errors here. */
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ {
+ struct symbol *vsym;
+ struct value *value;
+ CORE_ADDR handler;
+ struct breakpoint *bp;
+
+ vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+ value = read_var_value (vsym, frame);
+ /* If the value was optimized out, revert to the old behavior. */
+ if (! value_optimized_out (value))
+ {
+ handler = value_as_address (value);
+
+ /* We're going to replace the current step-resume breakpoint
+ with an exception-resume breakpoint. */
+ delete_step_resume_breakpoint (tp);
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: exception resume at %lx\n",
+ (unsigned long) handler);
+
+ bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
+ handler, bp_exception_resume);
+ inferior_thread ()->step_resume_breakpoint = bp;
+ }
+ }
+}
+
+/* This is called when an exception has been intercepted. Check to
+ see whether the exception's destination is of interest, and if so,
+ set an exception resume breakpoint there. */
+
+static void
+check_exception_resume (struct execution_control_state *ecs,
+ struct frame_info *frame, struct symbol *func)
+{
+ struct gdb_exception e;
+
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ {
+ struct block *b;
+ struct dict_iterator iter;
+ struct symbol *sym;
+ int argno = 0;
+
+ /* The exception breakpoint is a thread-specific breakpoint on
+ the unwinder's debug hook, declared as:
+
+ void _Unwind_DebugHook (void *cfa, void *handler);
+
+ The CFA argument indicates the frame to which control is
+ about to be transferred. HANDLER is the destination PC.
+
+ We ignore the CFA and set a temporary breakpoint at HANDLER.
+ This is not extremely efficient but it avoids issues in gdb
+ with computing the DWARF CFA, and it also works even in weird
+ cases such as throwing an exception from inside a signal
+ handler. */
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ ALL_BLOCK_SYMBOLS (b, iter, sym)
+ {
+ if (!SYMBOL_IS_ARGUMENT (sym))
+ continue;
+
+ if (argno == 0)
+ ++argno;
+ else
+ {
+ insert_exception_resume_breakpoint (ecs->event_thread,
+ b, frame, sym);
+ break;
+ }
+ }
+ }
+}
+
static void
stop_stepping (struct execution_control_state *ecs)
{
Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.cc
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.cc 2010-11-17 02:39:19.000000000 +0100
@@ -0,0 +1,180 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008, 2009 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1()
+ {
+ throw 20;
+ }
+
+ // Throw an exception in another function.
+ void function2()
+ {
+ function1();
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 ()
+ {
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow ()
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish ()
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 ();
+ }
+ catch (int x)
+ {
+ }
+ function1 (); // marker for until
+ }
+
+ void until ()
+ {
+ function1 ();
+ function1 (); // until here
+ }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main ()
+{
+ try
+ {
+ next_cases.function1 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // This is duplicated so we can next over one but step into
+ // another.
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ next_cases.function3 ();
+
+ try
+ {
+ next_cases.rethrow ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // Another duplicate so we can test "finish".
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ // Another test for "finish".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until" with an argument.
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "advance".
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+}
+
Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.exp 2010-11-17 02:39:19.000000000 +0100
@@ -0,0 +1,182 @@
+# Copyright 2008, 2009, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if { [skip_cplus_tests] } { continue }
+
+set testfile "gdb9593"
+set srcfile ${testfile}.cc
+set binfile $objdir/$subdir/$testfile
+
+# Create and source the file that provides information about the compiler
+# used to compile the test case.
+if [get_compiler_info ${binfile} "c++"] {
+ untested gdb9593.exp
+ return -1
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+ untested gdb9593.exp
+ return -1
+}
+
+# Some targets can't do function calls, so don't even bother with this
+# test.
+if [target_info exists gdb,cannot_call_functions] {
+ setup_xfail "*-*-*" 9593
+ fail "This target can not call functions"
+ continue
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+ perror "couldn't run to main"
+ continue
+}
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook"
+ }
+ -re "No symbol .* in current context.\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ pass "check for unwinder hook"
+ set ok 0
+ }
+}
+if {!$ok} {
+ untested gdb9593.exp
+ return -1
+}
+
+# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 1"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next past catch 1"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 2"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next past catch 2"
+
+gdb_test "step" \
+ ".*function1().*" \
+ "step into function2 1"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 3"
+
+gdb_test "next" \
+ ".*next_cases.function3.*" \
+ "next past catch 3"
+
+gdb_test "next" \
+ ".*next_cases.rethrow.*" \
+ "next over a throw 4"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a rethrow"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next after a rethrow"
+
+gdb_test "step" \
+ ".*function1().*" \
+ "step into function2 2"
+
+gdb_test "finish" \
+ ".*catch (...).*" \
+ "finish 1"
+
+gdb_test "next" \
+ ".*next_cases.finish ().*" \
+ "next past catch 4"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into finish method"
+
+gdb_test "finish" \
+ ".*catch (...).*" \
+ "finish 2"
+
+gdb_test "next" \
+ ".*next_cases.finish ().*" \
+ "next past catch 5"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into finish, for until"
+
+gdb_test "until" \
+ ".*catch .int x.*" \
+ "until with no argument 1"
+
+set line [gdb_get_line_number "marker for until" $testfile.cc]
+
+gdb_test "until $line" \
+ ".*function1 ().*" \
+ "next past catch 6"
+
+gdb_test "until" \
+ ".*catch (...).*" \
+ "until with no argument 2"
+
+set line [gdb_get_line_number "until here" $testfile.cc]
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 6"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until"
+
+gdb_test "until $line" \
+ ".*catch (...).*" \
+ "until-over-throw"
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 7"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until, for advance"
+
+gdb_test "advance $line" \
+ ".*catch (...).*" \
+ "advance-over-throw"
Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.exp
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.exp 2010-11-17 02:39:19.000000000 +0100
@@ -0,0 +1,74 @@
+# Copyright 2009, 2010 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 3 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, see <http://www.gnu.org/licenses/>.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+load_lib "java.exp"
+
+set testfile "jnpe"
+set srcfile ${testfile}.java
+set binfile ${objdir}/${subdir}/${testfile}
+if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
+ untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
+ return -1
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set line [gdb_get_line_number "break here" $testfile.java]
+gdb_test "break $testfile.java:$line" ""
+
+gdb_test "run" \
+ "// break here.*" \
+ "run java next-over-throw"
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook in java" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook in java"
+ }
+ -re "No symbol .* in current context.?\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ setup_xfail *-*-*
+ fail "check for unwinder hook in java"
+ set ok 0
+ }
+}
+if {!$ok} {
+ untested jnpe.exp
+ return -1
+}
+
+gdb_test "handle SIGSEGV nostop noprint" \
+ "SIGSEGV.*fault" \
+ "disable SIGSEGV for next-over-NPE"
+
+# The line where we stop differ according to gcj; check just we did not already
+# execute the catch point.
+
+gdb_test "next" \
+ "" \
+ "next over NPE"
+
+gdb_breakpoint [gdb_get_line_number "catch point"]
+gdb_continue_to_breakpoint "catch point" ".*// catch point.*"
Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.java
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.java 2010-11-17 02:39:19.000000000 +0100
@@ -0,0 +1,38 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+public class jnpe
+{
+ public static String npe ()
+ {
+ return ((Object) null).toString();
+ }
+
+ public static void main (String[] args)
+ {
+ try
+ {
+ System.out.println (npe ()); // break here
+ }
+ catch (NullPointerException n)
+ {
+ System.out.println ("success"); // catch point
+ }
+ }
+}