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

Tom Tromey
2010-12-09T16:37:47Z

>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:

This is my second post. The first one was rejected by sourceware for
being too spammy.

Tom> Here is an updated version of the next-over-throw patch.
Tom> I think I've addressed all of Jan's comments.

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

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

I rebased this patch on CVS HEAD and committed it.
I'm appending the updated version.

I built and regtested this version as well.

Tom

2010-12-09 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-09 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 1d66eef..6a51a3b 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2209,6 +2209,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)
{
@@ -2250,7 +2279,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;
@@ -2265,7 +2295,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;
@@ -2327,6 +2358,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
@@ -3248,6 +3280,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);
@@ -3335,6 +3373,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:
@@ -4121,7 +4161,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
@@ -4216,6 +4257,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)
{
@@ -4271,10 +4313,14 @@ bpstat_what (bpstat bs)
}
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)
@@ -4290,6 +4336,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:
@@ -4483,6 +4530,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"},
@@ -4492,6 +4541,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"},
@@ -4630,6 +4680,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:
@@ -4639,6 +4691,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:
@@ -5379,6 +5432,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:
@@ -5389,6 +5444,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:
@@ -5605,13 +5661,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
@@ -5619,13 +5676,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. */
@@ -5635,7 +5695,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);
@@ -6807,6 +6867,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:
@@ -6817,6 +6879,7 @@ mention (struct breakpoint *b)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}

@@ -8490,6 +8553,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
@@ -8504,6 +8568,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
@@ -8515,6 +8580,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 ();

@@ -8553,6 +8620,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. */

@@ -8565,6 +8635,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);
@@ -8581,6 +8654,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 (),
@@ -9820,6 +9894,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;
@@ -9841,6 +9916,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);
}
@@ -10301,6 +10377,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;

@@ -10324,6 +10401,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;
}
@@ -10367,6 +10446,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 0fb6830..a044c6b 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -57,6 +57,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,
@@ -126,6 +133,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,
@@ -665,6 +675,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,
@@ -925,7 +940,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 c6a1d5c..c3a49ef 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -40,6 +40,9 @@ struct thread_control_state
/* 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
@@ -207,6 +210,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;

@@ -243,6 +250,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 9664468..4016443 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);
}
@@ -1220,6 +1222,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.

@@ -1236,6 +1248,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 ();
@@ -1271,7 +1285,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
@@ -1464,6 +1489,7 @@ finish_command_continuation (void *arg)
if (bs != NULL && tp->control.proceed_to_finish)
observer_notify_normal_stop (bs, 1 /* print frame */);
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}

static void
@@ -1548,6 +1574,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);
@@ -1558,6 +1585,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);
+
/* We want stop_registers, please... */
tp->control.proceed_to_finish = 1;
cargs = xmalloc (sizeof (*cargs));
diff --git a/gdb/inferior.h b/gdb/inferior.h
index b23a03b..f80ecb5 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -279,6 +279,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 1604e95..1bc00a4 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"
@@ -377,6 +379,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 };
@@ -429,6 +432,8 @@ follow_fork (void)
step_range_start = tp->control.step_range_start;
step_range_end = tp->control.step_range_end;
step_frame_id = tp->control.step_frame_id;
+ exception_resume_breakpoint
+ = clone_momentary_breakpoint (tp->control.exception_resume_breakpoint);

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

parent = inferior_ptid;
@@ -481,6 +487,8 @@ follow_fork (void)
tp->control.step_range_start = step_range_start;
tp->control.step_range_end = step_range_end;
tp->control.step_frame_id = step_frame_id;
+ tp->control.exception_resume_breakpoint
+ = exception_resume_breakpoint;
}
else
{
@@ -534,6 +542,9 @@ follow_inferior_reset_breakpoints (void)
if (tp->control.step_resume_breakpoint)
breakpoint_re_set_thread (tp->control.step_resume_breakpoint);

+ if (tp->control.exception_resume_breakpoint)
+ breakpoint_re_set_thread (tp->control.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
@@ -771,6 +782,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->control.step_resume_breakpoint = NULL;
+ th->control.exception_resume_breakpoint = NULL;
th->control.step_range_start = 0;
th->control.step_range_end = 0;

@@ -2219,6 +2231,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);
@@ -2340,6 +2354,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;
}

@@ -2364,6 +2379,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
@@ -4112,23 +4128,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;

@@ -4137,9 +4163,54 @@ 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->control.step_resume_breakpoint
- != NULL);
- delete_step_resume_breakpoint (ecs->event_thread);
+ if (what.is_longjmp)
+ {
+ gdb_assert (ecs->event_thread->control.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->control.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->control.stop_step = 1;
print_end_stepping_range_reason ();
@@ -5109,6 +5180,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 ()->control.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/thread.c b/gdb/thread.c
index e23a784..1be325f 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->control.exception_resume_breakpoint)
+ {
+ delete_breakpoint (tp->control.exception_resume_breakpoint);
+ tp->control.exception_resume_breakpoint = NULL;
+ }
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
@@ -103,10 +113,19 @@ clear_thread_inferior_resources (struct thread_info *tp)
tp->control.step_resume_breakpoint = NULL;
}

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

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

static void