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

Tom Tromey
2009-06-12T20:49:14Z

>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:

Pedro> Which I guessed should be using frame_id comparisions, and
Pedro> something other than gdbarch_inner_than, frame_find_by_id
Pedro> perhaps (that does require a well behaved unwinder).

Here is an updated version of the patch.

This version uses frame_id comparisons, and it avoids the unwinder's
notion of the CFA. This means it is a bit less efficient (gdb must do
more work for every exception that is thrown), but also more robust in
the event of odd things, like exceptions from signal handlers.

Joel, I did look at the breakpoint_ops stuff a little, but I don't see
how it could really work for this situation. This really needs some
hooks into handle_inferior_event, and it didn't seem to me that any of
the breakpoint_ops methods were appropriate for this.

Note that this implementation also suffers from a bug affecting
longjmp, http://sourceware.org/bugzilla/show_bug.cgi?id=10236

I still haven't tried it with throw-from-signal-handler; I'll build a
new libgcj soon and try it there. Jakub also asked me to run a few
tests which I have not tried yet. So, this patch is not check-in
ready. Still, I would appreciate any comments you might have.

FWIW, I think the longjmp code ought to work the same way that this
code works. That is, record the initiating frame for an operation,
and ignore lonjmps whose target frame is newer than the initiating
frame. Users wanting the current longjmp behavior could easily
recover it by setting a breakpoint manually.

If this seems like an acceptable plan, I will change longjmp to work
the same way. Let me know what you think.

Tom

2009-06-12 Tom Tromey <tromey@redhat.com>

PR c++/9593:
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(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): Call set_exception_breakpoint.
(until_next_continuation): New function.
(until_next_command): Call set_exception_breakpoint. Make a
continuation.
(finish_command_continuation): Call delete_longjmp_breakpoint.
(finish_forward): Call set_exception_breakpoint.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume>:
New constants.
(struct bpstat_what) <is_longjmp>: New field.
* breakpoint.c (update_breakpoints_after_exec): Handle
bp_exception and bp_exception_resume.
(print_it_typical): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(mention): Likewise.
(breakpoint_re_set_one): Likewise.
(bpstat_what): Likewise. Set 'is_longjmp' field.
(delete_longjmp_breakpoint): Handle bp_exception.
(set_exception_breakpoint): New function.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Call set_exception_breakpoint.

2009-06-12 Tom Tromey <tromey@redhat.com>

* gdb.cp/gdb9593.cc: New file.
* gdb.cp/gdb9593.exp: New file.
* gdb.cp/Makefile.in (EXECUTABLES): Add gdb9593.

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 912d7ea..f02979e 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2,7 +2,7 @@

Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008 Free Software Foundation, Inc.
+ 2008, 2009 Free Software Foundation, Inc.

This file is part of GDB.

@@ -1480,7 +1480,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;
@@ -2427,6 +2428,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:
@@ -3266,6 +3269,7 @@ bpstat_what (bpstat bs)
struct bpstat_what retval;

retval.call_dummy = 0;
+ retval.is_longjmp = 0;
for (; bs != NULL; bs = bs->next)
{
enum class bs_class = no_effect;
@@ -3312,10 +3316,15 @@ bpstat_what (bpstat bs)
bs_class = no_effect;
break;
case bp_longjmp:
+ case bp_exception:
bs_class = long_jump;
+ retval.is_longjmp = bs->breakpoint_at->owner->type == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
bs_class = long_resume;
+ retval.is_longjmp
+ = bs->breakpoint_at->owner->type == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -3518,6 +3527,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"},
@@ -3669,6 +3680,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:
@@ -4244,6 +4257,8 @@ allocate_bp_location (struct breakpoint *bpt, enum bptype bp_type)
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:
@@ -4462,13 +4477,39 @@ 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);
}
}

+/* Install a breakpoint on the unwinder's debug hook, and arrange for
+ the unwinder to call the hook when unwinding to (or past) the
+ current frame. Returns the frame id corresponding to FRAME, or the
+ null frame id if no breakpoint was set. */
+
+struct frame_id
+set_exception_breakpoint (struct frame_info *frame)
+{
+ struct minimal_symbol *debug_hook;
+ struct frame_id result = null_frame_id;
+
+ debug_hook = lookup_minimal_symbol_text ("_Unwind_DebugHook", NULL);
+ if (debug_hook != NULL)
+ {
+ CORE_ADDR pc;
+
+ pc = find_function_start_pc (get_frame_arch (frame),
+ SYMBOL_VALUE_ADDRESS (debug_hook),
+ SYMBOL_OBJ_SECTION (debug_hook));
+ set_momentary_breakpoint_at_pc (pc, bp_exception);
+ result = get_frame_id (frame);
+ }
+
+ return result;
+}
+
static void
create_overlay_event_breakpoint_1 (char *func_name, struct objfile *objfile)
{
@@ -5158,6 +5199,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_watchpoint_scope:
@@ -6356,6 +6399,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
@@ -6370,6 +6414,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
@@ -6382,6 +6427,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 ();

@@ -6418,6 +6465,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. */
if (prev_frame)
@@ -6427,6 +6477,10 @@ until_break_command (char *arg, int from_tty, int anywhere)
breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ /* Install exception-handling breakpoint. */
+ tp->initiating_frame = set_exception_breakpoint (prev_frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}

proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -6443,6 +6497,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 (),
@@ -7777,6 +7832,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:
break;
}

diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index a77b1b4..bdbe3cc 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1,6 +1,6 @@
/* Data structures associated with breakpoints in GDB.
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
- 2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc.
+ 2002, 2003, 2004, 2007, 2008, 2009 Free Software Foundation, Inc.

This file is part of GDB.

@@ -55,6 +55,9 @@ enum bptype
bp_longjmp, /* secret breakpoint to find longjmp() */
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */

+ bp_exception,
+ 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,
@@ -554,6 +557,10 @@ struct bpstat_what
continuing from a call dummy without popping the frame is not a
useful one). */
int 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,
@@ -777,6 +784,8 @@ extern int detach_breakpoints (int);
extern void set_longjmp_breakpoint (void);
extern void delete_longjmp_breakpoint (int thread);

+extern struct frame_id set_exception_breakpoint (struct frame_info *);
+
extern void enable_overlay_breakpoints (void);
extern void disable_overlay_breakpoints (void);

diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 55c848d..9a66760 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -1,6 +1,6 @@
/* Multi-process/thread control defs for GDB, the GNU debugger.
Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1997, 1998, 1999,
- 2000, 2007, 2008 Free Software Foundation, Inc.
+ 2000, 2007, 2008, 2009 Free Software Foundation, Inc.
Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.

@@ -171,6 +171,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;
};
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 50e8fff..7e60d0b 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2,7 +2,7 @@

Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008 Free Software Foundation, Inc.
+ 2008, 2009 Free Software Foundation, Inc.

This file is part of GDB.

@@ -754,7 +754,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;
@@ -793,11 +793,13 @@ 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 ();
-
+ tp->initiating_frame = set_exception_breakpoint (get_current_frame ());
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}

@@ -1154,6 +1156,15 @@ signal_command (char *signum_exp, int from_tty)
proceed (oursig == TARGET_SIGNAL_0 ? (CORE_ADDR) -1 : stop_pc, 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.

@@ -1170,6 +1181,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 ();

@@ -1205,7 +1218,18 @@ until_next_command (int from_tty)

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

+ tp->initiating_frame = set_exception_breakpoint (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
@@ -1383,6 +1407,7 @@ finish_command_continuation (void *arg)
observer_notify_normal_stop (bs);
suppress_stop_observer = 0;
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}

static void
@@ -1463,6 +1488,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);
@@ -1472,6 +1498,9 @@ finish_forward (struct symbol *function, struct frame_info *frame)

old_chain = make_cleanup_delete_breakpoint (breakpoint);

+ tp->initiating_frame = set_exception_breakpoint (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
make_cleanup_restore_integer (&suppress_stop_observer);
suppress_stop_observer = 1;
diff --git a/gdb/inferior.h b/gdb/inferior.h
index cc5bf9f..3e62a2f 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -2,7 +2,7 @@
Where it is, why it stopped, and how to step it.

Copyright (C) 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
- 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008
+ 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.

This file is part of GDB.
@@ -269,6 +269,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);
+
/* Address at which inferior stopped. */

extern CORE_ADDR stop_pc;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 30d914d..4686606 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3,7 +3,7 @@

Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
- 2008 Free Software Foundation, Inc.
+ 2008, 2009 Free Software Foundation, Inc.

This file is part of GDB.

@@ -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"
@@ -1551,6 +1553,8 @@ static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
static void insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (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);
@@ -3019,24 +3023,34 @@ process_event_stop_test:

ecs->event_thread->stepping_over_breakpoint = 1;

- if (!gdbarch_get_longjmp_target_p (current_gdbarch)
- || !gdbarch_get_longjmp_target (current_gdbarch,
- get_current_frame (), &jmp_buf_pc))
+ if (what.is_longjmp)
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
+ if (!gdbarch_get_longjmp_target_p (current_gdbarch)
+ || !gdbarch_get_longjmp_target (current_gdbarch,
+ get_current_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 (jmp_buf_pc);
+ /* Insert a breakpoint at resume address. */
+ insert_longjmp_resume_breakpoint (jmp_buf_pc);
+ }
+ else
+ {
+ struct frame_info *frame = get_current_frame ();
+ struct symbol *func = get_frame_function (frame);

+ if (func)
+ check_exception_resume (ecs, frame, func);
+ }
keep_going (ecs);
return;

@@ -3048,6 +3062,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
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_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
@@ -3851,6 +3912,95 @@ insert_longjmp_resume_breakpoint (CORE_ADDR pc)
set_momentary_breakpoint_at_pc (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);
+ 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 (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)
{
@@ -3917,6 +4067,8 @@ keep_going (struct execution_control_state *ecs)
}
if (e.reason < 0)
{
+ if (debug_infrun)
+ exception_fprintf (gdb_stdlog, e, "infrun: exception while inserting breakpoints: ");
stop_stepping (ecs);
return;
}
diff --git a/gdb/testsuite/gdb.cp/Makefile.in b/gdb/testsuite/gdb.cp/Makefile.in
index f4a989c..1a2b908 100644
--- a/gdb/testsuite/gdb.cp/Makefile.in
+++ b/gdb/testsuite/gdb.cp/Makefile.in
@@ -4,7 +4,7 @@ srcdir = @srcdir@
EXECUTABLES = ambiguous annota2 anon-union cplusfuncs cttiadd \
derivation inherit local member-ptr method misc \
overload ovldbreak ref-typ ref-typ2 templates userdef virtfunc namespace \
- ref-types ref-params method2
+ ref-types ref-params method2 gdb9593

all info install-info dvi install uninstall installcheck check:
@echo "Nothing to be done for $@..."
diff --git a/gdb/testsuite/gdb.cp/gdb9593.cc b/gdb/testsuite/gdb.cp/gdb9593.cc
new file mode 100644
index 0000000..3092f57
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.cc
@@ -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 ();
+ }
+
+ 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 (...)
+ {
+ }
+}
+
diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
new file mode 100644
index 0000000..b0fcfbc
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.exp
@@ -0,0 +1,183 @@
+# 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/>.
+
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if { [skip_cplus_tests] } { continue }
+
+set prms_id 9593
+set bug_id 0
+
+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"
+
+gdb_test "next" \
+ ".*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"