Re: RFC: next/finish/etc -vs- exceptions

Tom Tromey
2010-12-02T15:32:58Z

Here is an updated version of the next-over-throw patch.

I think I've addressed all of Jan's comments.

Built and regtested on x86-64 (compile farm).
I also did various tests on PPC32 and PPC64 machines.

Let me know what you think.
In the absence of comments I will check this in next week.

Tom

2010-12-02 Tom Tromey <tromey@redhat.com>

PR c++/9593:
* thread.c (clear_thread_inferior_resources): Call
delete_longjmp_breakpoint.
* 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.
(set_longjmp_breakpoint): Update.
* 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. Change
interface.
(delete_longjmp_breakpoint): Handle exception breakpoints.
(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-12-02 Tom Tromey <tromey@redhat.com>

* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* gdb.cp/nextoverthrow.exp: New file.
* gdb.cp/nextoverthrow.cc: New file.

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 4affe0a..11bfc11 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2201,6 +2201,35 @@ create_std_terminate_master_breakpoint (const char *func_name)
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 ("_Unwind_DebugHook", NULL, objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+ CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (debug_hook);
+ struct gdbarch *gdbarch = get_objfile_arch (objfile);
+
+ addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+ &current_target);
+ b = create_internal_breakpoint (gdbarch, addr, 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)
{
@@ -2242,7 +2271,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;
@@ -2257,7 +2287,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;
@@ -2319,6 +2350,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
@@ -3237,6 +3269,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);
@@ -3324,6 +3362,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:
@@ -4075,7 +4115,8 @@ bpstat_stop_status (struct address_space *aspace,

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
@@ -4187,6 +4228,7 @@ bpstat_what (bpstat bs)

retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;

for (; bs != NULL; bs = bs->next)
{
@@ -4242,10 +4284,15 @@ bpstat_what (bpstat bs)
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bs->breakpoint_at->owner->type == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp
+ = bs->breakpoint_at->owner->type == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -4261,6 +4308,7 @@ bpstat_what (bpstat bs)
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:
@@ -4461,6 +4509,8 @@ print_one_breakpoint_location (struct breakpoint *b,
{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"},
@@ -4470,6 +4520,7 @@ print_one_breakpoint_location (struct breakpoint *b,
{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"},
@@ -4595,6 +4646,8 @@ print_one_breakpoint_location (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_watchpoint_scope:
case bp_call_dummy:
@@ -4604,6 +4657,7 @@ print_one_breakpoint_location (struct breakpoint *b,
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:
@@ -5334,6 +5388,8 @@ allocate_bp_location (struct breakpoint *bpt)
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:
@@ -5344,6 +5400,7 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -5548,13 +5605,14 @@ make_breakpoint_permanent (struct breakpoint *b)
}

/* 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 TP. FRAME is the frame which
+ initiated the operation. */

void
-set_longjmp_breakpoint (int thread)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
{
struct breakpoint *b, *temp;
+ int thread = tp->num;

/* To avoid having to rescan all objfile symbols at every step,
we maintain a list of continually-inserted but always disabled
@@ -5562,13 +5620,16 @@ 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;
}
+
+ tp->initiating_frame = frame;
}

/* Delete all longjmp breakpoints from THREAD. */
@@ -5578,7 +5639,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);
@@ -6750,6 +6811,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:
@@ -6760,6 +6823,7 @@ mention (struct breakpoint *b)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}

@@ -8346,6 +8410,7 @@ struct until_break_command_continuation_args
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};

/* This function is called by fetch_inferior_event via the
@@ -8360,6 +8425,7 @@ until_break_command_continuation (void *arg)
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}

void
@@ -8371,6 +8437,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;

clear_proceed_status ();

@@ -8409,6 +8477,9 @@ until_break_command (char *arg, int from_tty, int anywhere)

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. */

@@ -8421,6 +8492,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}

proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8437,6 +8511,7 @@ until_break_command (char *arg, int from_tty, int anywhere)

args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;

discard_cleanups (old_chain);
add_continuation (inferior_thread (),
@@ -9658,6 +9733,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;
@@ -9679,6 +9755,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);
}
@@ -10139,6 +10216,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;

@@ -10162,6 +10240,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:
break;
}
@@ -10205,6 +10285,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 ();
}
\f
/* Reset the thread number of this breakpoint:
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 6f5d050..6eadb34 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -56,6 +56,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,
@@ -125,6 +132,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,
@@ -653,6 +663,11 @@ 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 and
+ BPSTAT_WHAT_CLEAR_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,
@@ -887,7 +902,8 @@ extern int detach_breakpoints (int);
this PSPACE anymore. */
extern void breakpoint_program_space_exit (struct program_space *pspace);

-extern void set_longjmp_breakpoint (int thread);
+extern void set_longjmp_breakpoint (struct thread_info *tp,
+ struct frame_id frame);
extern void delete_longjmp_breakpoint (int thread);

extern void enable_overlay_breakpoints (void);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index cd24eaf..9638368 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -66,6 +66,9 @@ struct thread_info
/* Step-resume or longjmp-resume breakpoint. */
struct breakpoint *step_resume_breakpoint;

+ /* Exception-resume breakpoint. */
+ struct breakpoint *exception_resume_breakpoint;
+
/* Range to single step within.

If this is nonzero, respond to a single-step signal by continuing
@@ -185,6 +188,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;

@@ -221,6 +228,9 @@ extern void delete_thread_silent (ptid_t);
/* Delete a step_resume_breakpoint from the thread database. */
extern void delete_step_resume_breakpoint (struct thread_info *);

+/* Delete an exception_resume_breakpoint from the thread database. */
+extern void delete_exception_resume_breakpoint (struct thread_info *);
+
/* Translate the integer thread id (GDB's homegrown id, not the system's)
into a "pid" (which may be overloaded with extra thread information). */
extern ptid_t thread_id_to_pid (int);
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c4cdb06..ca7d094 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -822,7 +822,7 @@ nexti_command (char *count_string, int from_tty)
step_1 (1, 1, count_string);
}

-static void
+void
delete_longjmp_breakpoint_cleanup (void *arg)
{
int thread = * (int *) arg;
@@ -862,10 +862,12 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)

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);
+ set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));

make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1219,6 +1221,16 @@ signal_command (char *signum_exp, int from_tty)
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,18 @@ until_next_command (int from_tty)

tp->step_multi = 0; /* Only one call to proceed */

+ set_longjmp_breakpoint (tp, 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 +1488,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 +1572,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
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 +1583,9 @@ finish_forward (struct symbol *function, struct frame_info *frame)

old_chain = make_cleanup_delete_breakpoint (breakpoint);

+ set_longjmp_breakpoint (tp, 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));

diff --git a/gdb/inferior.h b/gdb/inferior.h
index 5abec68..e309277 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -291,6 +291,8 @@ extern void interrupt_target_command (char *args, int from_tty);

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);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 57f2ab8..f6464d4 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -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"
@@ -367,6 +369,7 @@ follow_fork (void)
parent thread structure's run control related fields, not just these.
Initialized to avoid "may be used uninitialized" warnings from gcc. */
struct breakpoint *step_resume_breakpoint = NULL;
+ struct breakpoint *exception_resume_breakpoint = NULL;
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
struct frame_id step_frame_id = { 0 };
@@ -419,6 +422,8 @@ follow_fork (void)
step_range_start = tp->step_range_start;
step_range_end = tp->step_range_end;
step_frame_id = tp->step_frame_id;
+ exception_resume_breakpoint
+ = clone_momentary_breakpoint (tp->exception_resume_breakpoint);

/* For now, delete the parent's sr breakpoint, otherwise,
parent/child sr breakpoints are considered duplicates,
@@ -429,6 +434,7 @@ follow_fork (void)
tp->step_range_start = 0;
tp->step_range_end = 0;
tp->step_frame_id = null_frame_id;
+ delete_exception_resume_breakpoint (tp);
}

parent = inferior_ptid;
@@ -470,6 +476,8 @@ follow_fork (void)
tp->step_range_start = step_range_start;
tp->step_range_end = step_range_end;
tp->step_frame_id = step_frame_id;
+ tp->exception_resume_breakpoint
+ = exception_resume_breakpoint;
}
else
{
@@ -523,6 +531,9 @@ follow_inferior_reset_breakpoints (void)
if (tp->step_resume_breakpoint)
breakpoint_re_set_thread (tp->step_resume_breakpoint);

+ if (tp->exception_resume_breakpoint)
+ breakpoint_re_set_thread (tp->exception_resume_breakpoint);
+
/* Reinsert all breakpoints in the child. The user may have set
breakpoints after catching the fork, in which case those
were never set in the child, but only in the parent. This makes
@@ -760,6 +771,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
/* If there was one, it's gone now. We cannot truly step-to-next
statement through an exec(). */
th->step_resume_breakpoint = NULL;
+ th->exception_resume_breakpoint = NULL;
th->step_range_start = 0;
th->step_range_end = 0;

@@ -2190,6 +2202,8 @@ static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
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);
@@ -2313,6 +2327,7 @@ delete_step_resume_breakpoint_callback (struct thread_info *info, void *data)
return 0;

delete_step_resume_breakpoint (info);
+ delete_exception_resume_breakpoint (info);
return 0;
}

@@ -2337,6 +2352,7 @@ delete_step_thread_step_resume_breakpoint (void)
struct thread_info *tp = inferior_thread ();

delete_step_resume_breakpoint (tp);
+ delete_exception_resume_breakpoint (tp);
}
else
/* In all-stop mode, delete all step-resume and longjmp-resume
@@ -4076,23 +4092,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;

@@ -4101,8 +4127,52 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
fprintf_unfiltered (gdb_stdlog,
"infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");

- gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
- delete_step_resume_breakpoint (ecs->event_thread);
+ if (what.is_longjmp)
+ {
+ gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }
+ else
+ {
+ /* 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. We stop, because the exception has been
+ caught.
+
+ 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);
+
+ gdb_assert (ecs->event_thread->exception_resume_breakpoint != NULL);
+ delete_exception_resume_breakpoint (ecs->event_thread);
+
+ 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))
+ {
+ /* Case 2. Fall through. */
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+
+ /* For Cases 1 and 2, remove the step-resume breakpoint,
+ if it exists. */
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }

ecs->event_thread->stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
@@ -5070,6 +5140,97 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
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_ERROR)
+ {
+ 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);
+
+ 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);
+ bp->thread = tp->num;
+ inferior_thread ()->exception_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_ERROR)
+ {
+ 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)
{
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.cc b/gdb/testsuite/gdb.cp/nextoverthrow.cc
new file mode 100644
index 0000000..b25cb34
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -0,0 +1,203 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ 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/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+void dummy ()
+{
+}
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1 (int val)
+ {
+ throw val;
+ }
+
+ // Throw an exception in another function.
+ void function2 (int val)
+ {
+ function1 (val);
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 (int val)
+ {
+ {
+ try
+ {
+ function1 (val);
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow (int val)
+ {
+ try
+ {
+ function1 (val);
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish (int val)
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 (val);
+ }
+ catch (int x)
+ {
+ }
+ function1 (val); // marker for until
+ }
+
+ void until (int val)
+ {
+ function1 (val);
+ function1 (val); // until here
+ }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main ()
+{
+ int testval = -1;
+
+ try
+ {
+ next_cases.function1 (0); // Start: first test
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first test
+ }
+
+ try
+ {
+ next_cases.function2 (1); // Start: nested throw
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: nested throw
+ }
+
+ try
+ {
+ // This is duplicated so we can next over one but step into
+ // another.
+ next_cases.function2 (2); // Start: step in test
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: step in test
+ }
+
+ next_cases.function3 (3); // Start: next past catch
+ dummy ();
+ testval = 3; // End: next past catch
+
+ try
+ {
+ next_cases.rethrow (4); // Start: rethrow
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: rethrow
+ }
+
+ try
+ {
+ // Another duplicate so we can test "finish".
+ next_cases.function2 (5); // Start: first finish
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first finish
+ }
+
+ // Another test for "finish".
+ try
+ {
+ next_cases.finish (6); // Start: second finish
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: second finish
+ }
+
+ // Test of "until".
+ try
+ {
+ next_cases.finish (7); // Start: first until
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first until
+ }
+
+ // Test of "until" with an argument.
+ try
+ {
+ next_cases.until (8); // Start: second until
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: second until
+ }
+
+ // Test of "advance".
+ try
+ {
+ next_cases.until (9); // Start: advance
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: advance
+ }
+
+ testval = 32; // done
+}
+
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
new file mode 100644
index 0000000..960ea0d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -0,0 +1,153 @@
+# 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 "nextoverthrow"
+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 nextoverthrow.exp
+ return -1
+}
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+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} {
+ unsupported "nextoverthrow.exp could not find _Unwind_DebugHook"
+ return -1
+}
+
+# Set a temporary breakpoint and then continue to it.
+# The breakpoint is set according to a marker in the file.
+proc tbreak_and_cont {text} {
+ global testfile
+
+ set line [gdb_get_line_number $text $testfile.cc]
+ gdb_breakpoint "$testfile.cc:$line" temporary
+ gdb_test "continue" "Temporary breakpoint.*" "continuing to $text"
+}
+
+# Verify the value of testval.
+proc verify_testval {name value} {
+ gdb_test "print testval == $value" " = true" $name
+}
+
+# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
+# Our general approach here is to do some operation, verify
+# that testval has not changed, continue to the location at
+# which the next test starts, and verify testval again.
+# This works around platform differences in debuginfo that
+# make looking at the source line unreliable.
+
+# A simple test of next over a throw.
+tbreak_and_cont "Start: first test"
+gdb_test "next" ".*" "next over a throw 1"
+tbreak_and_cont "End: first test"
+verify_testval "pre-check - next over a throw 1" -1
+
+tbreak_and_cont "Start: nested throw"
+verify_testval "post-check - next over a throw 1" 0
+gdb_test "next" ".*" "next over a throw 2"
+tbreak_and_cont "End: nested throw"
+verify_testval "pre-check - next over a throw 2" 0
+
+tbreak_and_cont "Start: step in test"
+verify_testval "post-check - next over a throw 2" 1
+gdb_test "step" "function1().*" "step into function2 1"
+gdb_test "next" ".*" "next over a throw 3"
+tbreak_and_cont "End: step in test"
+verify_testval "pre-check - next over a throw 3" 1
+
+tbreak_and_cont "Start: next past catch"
+verify_testval "post-check - next over a throw 3" 2
+gdb_test "next" ".*" "next past catch"
+tbreak_and_cont "End: next past catch"
+verify_testval "pre-check - next past catch" 2
+
+tbreak_and_cont "Start: rethrow"
+verify_testval "post-check - next past catch" 3
+gdb_test "next" ".*" "next over a throw 4"
+tbreak_and_cont "End: rethrow"
+verify_testval "pre-check - next over a throw 4" 3
+
+tbreak_and_cont "Start: first finish"
+verify_testval "post-check - next over a throw 4" 4
+gdb_test "step" "function1().*" "step into function2 2"
+gdb_test "finish" ".*" "finish 1"
+tbreak_and_cont "End: first finish"
+verify_testval "pre-check - finish 1" 4
+
+tbreak_and_cont "Start: second finish"
+verify_testval "post-check - finish 1" 5
+gdb_test "step" "function1 ().*" "step into finish method"
+gdb_test "finish" ".*" "finish 2"
+tbreak_and_cont "End: second finish"
+verify_testval "pre-check - finish 2" 5
+
+tbreak_and_cont "Start: first until"
+verify_testval "post-check - finish 2" 6
+gdb_test "step" ".*" "step into finish, for until"
+gdb_test "until" ".*" "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" ".*" "until with no argument 2"
+tbreak_and_cont "End: first until"
+verify_testval "pre-check - until 1" 6
+
+tbreak_and_cont "Start: second until"
+verify_testval "post-check - until 1" 7
+set line [gdb_get_line_number "until here" $testfile.cc]
+gdb_test "step" "function1 ().*" "step into until"
+gdb_test "until $line" ".*" "until-over-throw"
+tbreak_and_cont "End: second until"
+verify_testval "pre-check - until 2" 7
+
+tbreak_and_cont "Start: advance"
+verify_testval "post-check - until 2" 8
+gdb_test "step" "function1 ().*" "step into until, for advance"
+gdb_test "advance $line" ".*" "advance-over-throw"
+tbreak_and_cont "End: advance"
+verify_testval "pre-check - advance" 8
+
+tbreak_and_cont "done"
+verify_testval "post-check - advance" 9
diff --git a/gdb/testsuite/gdb.java/jnpe.exp b/gdb/testsuite/gdb.java/jnpe.exp
new file mode 100644
index 0000000..f2895f6
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.exp
@@ -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.
+
+clean_restart $testfile
+
+set line [gdb_get_line_number "break here" $testfile.java]
+if ![runto "$testfile.java:$line"] then {
+ perror "couldn't run to jnpe.main"
+ continue
+}
+
+# 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} {
+ unsupported "jnpe.exp could not find _Unwind_DebugHook"
+ return -1
+}
+
+gdb_test "handle SIGSEGV nostop noprint" \
+ "SIGSEGV.*fault" \
+ "disable SIGSEGV for next-over-NPE"
+
+# The line where we stop differs according to gcj; just check that we
+# did not already execute the catch point. This is done in a somewhat
+# funny way due to other gcj debuginfo oddities that don't
+# meaningfully affect the user's experience.
+
+gdb_test "next" \
+ ".*" \
+ "next over NPE"
+
+set line [gdb_get_line_number "stop point"]
+gdb_breakpoint $line
+gdb_test "continue" "Continuing.\[\r\n\]*success\[\r\n\]*Breakpoint .*:$line\[\r\n\]*.*// stop point\[\r\n\]*" \
+ "continue to success for next-over-NPE"
diff --git a/gdb/testsuite/gdb.java/jnpe.java b/gdb/testsuite/gdb.java/jnpe.java
new file mode 100644
index 0000000..be096ce
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.java
@@ -0,0 +1,40 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+ 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/>.
+ */
+
+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");
+ }
+
+ System.out.println ("blah"); // stop point
+ }
+}
diff --git a/gdb/thread.c b/gdb/thread.c
index 2fdc7f9..40ed6d9 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -90,6 +90,16 @@ delete_step_resume_breakpoint (struct thread_info *tp)
}
}

+void
+delete_exception_resume_breakpoint (struct thread_info *tp)
+{
+ if (tp && tp->exception_resume_breakpoint)
+ {
+ delete_breakpoint (tp->exception_resume_breakpoint);
+ tp->exception_resume_breakpoint = NULL;
+ }
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
@@ -103,10 +113,18 @@ clear_thread_inferior_resources (struct thread_info *tp)
tp->step_resume_breakpoint = NULL;
}

+ if (tp->exception_resume_breakpoint)
+ {
+ tp->exception_resume_breakpoint->disposition = disp_del_at_next_stop;
+ tp->exception_resume_breakpoint = NULL;
+ }
+
bpstat_clear (&tp->stop_bpstat);

discard_all_intermediate_continuations_thread (tp);
discard_all_continuations_thread (tp);
+
+ delete_longjmp_breakpoint (tp->num);
}

static void