public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* RFC: next/finish/etc -vs- exceptions
@ 2010-10-07  1:37 Tom Tromey
  2010-11-24 17:53 ` Joel Brobecker
                   ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Tom Tromey @ 2010-10-07  1:37 UTC (permalink / raw)
  To: gdb-patches

This is a refresh of:

    http://www.sourceware.org/ml/gdb-patches/2009-05/msg00635.html

(The thread continues in the following month.)

I rebased the patch and addressed Pedro's comments.

I did not add the user notification that Doug asked for.

This version does work if an exception is thrown from a signal handler.
The included Java test case checks that (on most platforms).

Built and regtested on x86-64 (compile farm).
Tests for the new functionality are included.

I'd appreciate comments.  I don't want to commit this without a review,
as my experience is that I usually err when touching breakpoints or
infrun.

If you want to try this yourself, you will need a new-enough libgcc, one
that includes the necessary debugging hook.

Tom

2010-10-06  Tom Tromey  <tromey@redhat.com>

	* infrun.c (handle_inferior_event): Handle exception breakpoints.
	(handle_inferior_event): Likewise.
	(insert_exception_resume_breakpoint): New function.
	(check_exception_resume): Likewise.
	* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
	* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
	(step_1): Set thread's initiating frame.
	(until_next_continuation): New function.
	(until_next_command): Support exception breakpoints.
	(finish_command_continuation): Delete longjmp breakpoint.
	(finish_forward): Support exception breakpoints.
	* gdbthread.h (struct thread_info) <initiating_frame>: New field.
	* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
	bp_exception_master>: New constants.
	(struct bpstat_what) <is_longjmp>: New field.
	* breakpoint.c (create_exception_master_breakpoint): New function.
	(update_breakpoints_after_exec): Handle bp_exception_master.  Call
	create_exception_master_breakpoint.
	(print_it_typical): Handle bp_exception_master, bp_exception.
	(bpstat_stop_status): Handle bp_exception_master.
	(bpstat_what): Handle bp_exception_master, bp_exception,
	bp_exception_resume.
	(bptype_string): Likewise.
	(print_one_breakpoint_location): Likewise.
	(allocate_bp_location): Likewise.
	(set_longjmp_breakpoint): Handle exception breakpoints.
	(delete_longjmp_breakpoint): Likewise.
	(mention): Likewise.
	(struct until_break_command_continuation_args) <thread_num>: New
	field.
	(until_break_command_continuation): Call
	delete_longjmp_breakpoint.
	(until_break_command): Support exception breakpoints.
	(delete_command): Likewise.
	(breakpoint_re_set_one): Likewise.
	(breakpoint_re_set): Likewise.

2010-10-06  Tom Tromey  <tromey@redhat.com>

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

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b4502e7..345556c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2191,6 +2191,33 @@ 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_text ("_Unwind_DebugHook", objfile);
+      if (debug_hook != NULL)
+	{
+	  struct breakpoint *b;
+
+	  b = create_internal_breakpoint (get_objfile_arch (objfile),
+					  SYMBOL_VALUE_ADDRESS (debug_hook),
+					  bp_exception_master);
+	  b->addr_string = xstrdup ("_Unwind_DebugHook");
+	  b->enable_state = bp_disabled;
+	}
+    }
+
+  update_global_location_list (1);
+}
+
 void
 update_breakpoints_after_exec (void)
 {
@@ -2232,7 +2259,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;
@@ -2247,7 +2275,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;
@@ -2309,6 +2338,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
@@ -3230,6 +3260,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);
@@ -3317,6 +3353,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:
@@ -4103,7 +4141,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
@@ -4198,6 +4237,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)
     {
@@ -4253,10 +4293,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)
@@ -4272,6 +4316,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:
@@ -4465,6 +4510,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"},
@@ -4474,6 +4521,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"},
@@ -4612,6 +4660,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:
@@ -4621,6 +4671,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:
@@ -5359,6 +5410,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:
@@ -5369,6 +5422,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:
@@ -5584,8 +5638,7 @@ 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 THREAD.  */
 
 void
 set_longjmp_breakpoint (int thread)
@@ -5598,11 +5651,12 @@ set_longjmp_breakpoint (int thread)
      clones of those and enable them for the requested thread.  */
   ALL_BREAKPOINTS_SAFE (b, temp)
     if (b->pspace == current_program_space
-	&& b->type == bp_longjmp_master)
+	&& (b->type == bp_longjmp_master
+	    || b->type == bp_exception_master))
       {
 	struct breakpoint *clone = clone_momentary_breakpoint (b);
 
-	clone->type = bp_longjmp;
+	clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
 	clone->thread = thread;
       }
 }
@@ -5614,7 +5668,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);
@@ -6786,6 +6840,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:
@@ -6796,6 +6852,7 @@ mention (struct breakpoint *b)
       case bp_jit_event:
       case bp_longjmp_master:
       case bp_std_terminate_master:
+      case bp_exception_master:
 	break;
       }
 
@@ -8449,6 +8506,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
@@ -8463,6 +8521,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
@@ -8474,6 +8533,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 ();
 
@@ -8512,6 +8573,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.  */
 
@@ -8524,6 +8588,10 @@ 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 (thread);
+      tp->initiating_frame = frame_unwind_caller_id (frame);
+      make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
 
   proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8540,6 +8608,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 (),
@@ -9778,6 +9847,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;
@@ -9799,6 +9869,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);
 	  }
@@ -10259,6 +10330,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;
 
@@ -10282,6 +10354,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;
     }
@@ -10325,6 +10399,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 9f7600a..4f1163f 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,
@@ -657,6 +667,10 @@ struct bpstat_what
        continuing from a call dummy without popping the frame is not a
        useful one).  */
     enum stop_stack_kind call_dummy;
+
+    /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME.  True if we are
+       handling a longjmp, false if we are handling an exception.  */
+    int is_longjmp;
   };
 
 /* The possible return values for print_bpstat, print_it_normal,
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index cd24eaf..611dcbb 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -185,6 +185,10 @@ struct thread_info
   /* True if this thread has been explicitly requested to stop.  */
   int stop_requested;
 
+  /* The initiating frame of a nexting operation, used for deciding
+     which exceptions to intercept.  */
+  struct frame_id initiating_frame;
+
   /* Private data used by the target vector implementation.  */
   struct private_thread_info *private;
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c4cdb06..d213f6a 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,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 (thread);
+      tp->initiating_frame = get_frame_id (get_current_frame ());
 
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
@@ -1219,6 +1222,15 @@ signal_command (char *signum_exp, int 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,19 @@ until_next_command (int from_tty)
 
   tp->step_multi = 0;		/* Only one call to proceed */
 
+  set_longjmp_breakpoint (thread);
+  tp->initiating_frame = get_frame_id (frame);
+  old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+  if (target_can_async_p () && is_running (inferior_ptid))
+    {
+      discard_cleanups (old_chain);
+      add_continuation (tp, until_next_continuation, tp, NULL);
+    }
+  else
+    do_cleanups (old_chain);
 }
 
 static void
@@ -1463,6 +1489,7 @@ finish_command_continuation (void *arg)
   if (bs != NULL && tp->proceed_to_finish)
     observer_notify_normal_stop (bs, 1 /* print frame */);
   delete_breakpoint (a->breakpoint);
+  delete_longjmp_breakpoint (inferior_thread ()->num);
 }
 
 static void
@@ -1546,6 +1573,7 @@ finish_forward (struct symbol *function, struct 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 +1584,10 @@ finish_forward (struct symbol *function, struct frame_info *frame)
 
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
+  set_longjmp_breakpoint (thread);
+  tp->initiating_frame = get_frame_id (frame);
+  make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
   tp->proceed_to_finish = 1;    /* We want stop_registers, please...  */
   cargs = xmalloc (sizeof (*cargs));
 
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 0720b31..f75a52f 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"
@@ -2209,6 +2211,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);
@@ -4093,23 +4097,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;
 
@@ -4121,6 +4135,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_end_stepping_range_reason ();
 	stop_stepping (ecs);
@@ -5087,6 +5148,100 @@ 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_ALL)
+    {
+      struct symbol *vsym;
+      struct value *value;
+      CORE_ADDR handler;
+      struct breakpoint *bp;
+
+      vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+      value = read_var_value (vsym, frame);
+      /* If the value was optimized out, revert to the old behavior.  */
+      if (! value_optimized_out (value))
+	{
+	  handler = value_as_address (value);
+
+	  /* We're going to replace the current step-resume breakpoint
+	     with an exception-resume breakpoint.  */
+	  delete_step_resume_breakpoint (tp);
+
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: exception resume at %lx\n",
+				(unsigned long) handler);
+
+	  bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
+					       handler, bp_exception_resume);
+	  inferior_thread ()->step_resume_breakpoint = bp;
+	}
+    }
+}
+
+/* This is called when an exception has been intercepted.  Check to
+   see whether the exception's destination is of interest, and if so,
+   set an exception resume breakpoint there.  */
+
+static void
+check_exception_resume (struct execution_control_state *ecs,
+			struct frame_info *frame, struct symbol *func)
+{
+  struct gdb_exception e;
+
+  TRY_CATCH (e, RETURN_MASK_ALL)
+    {
+      struct block *b;
+      struct dict_iterator iter;
+      struct symbol *sym;
+      int argno = 0;
+
+      /* The exception breakpoint is a thread-specific breakpoint on
+	 the unwinder's debug hook, declared as:
+	 
+	 void _Unwind_DebugHook (void *cfa, void *handler);
+	 
+	 The CFA argument indicates the frame to which control is
+	 about to be transferred.  HANDLER is the destination PC.
+	 
+	 We ignore the CFA and set a temporary breakpoint at HANDLER.
+	 This is not extremely efficient but it avoids issues in gdb
+	 with computing the DWARF CFA, and it also works even in weird
+	 cases such as throwing an exception from inside a signal
+	 handler.  */
+
+      b = SYMBOL_BLOCK_VALUE (func);
+      ALL_BLOCK_SYMBOLS (b, iter, sym)
+	{
+	  if (!SYMBOL_IS_ARGUMENT (sym))
+	    continue;
+
+	  if (argno == 0)
+	    ++argno;
+	  else
+	    {
+	      insert_exception_resume_breakpoint (ecs->event_thread,
+						  b, frame, sym);
+	      break;
+	    }
+	}
+    }
+}
+
 static void
 stop_stepping (struct execution_control_state *ecs)
 {
diff --git a/gdb/testsuite/gdb.cp/gdb9593.cc b/gdb/testsuite/gdb.cp/gdb9593.cc
new file mode 100644
index 0000000..783c962
--- /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 ();		// marker for until
+  }
+
+  void until ()
+  {
+    function1 ();
+    function1 ();		// until here
+  }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main () 
+{ 
+  try
+    {
+      next_cases.function1 ();
+    }
+  catch (...)
+    {
+      // Discard
+    }
+
+  try
+    {
+      next_cases.function2 ();
+    }
+  catch (...)
+    {
+      // Discard
+    }
+
+  try
+    {
+      // This is duplicated so we can next over one but step into
+      // another.
+      next_cases.function2 ();
+    }
+  catch (...)
+    {
+      // Discard
+    }
+
+  next_cases.function3 ();
+
+  try
+    {
+      next_cases.rethrow ();
+    }
+  catch (...)
+    {
+      // Discard
+    }
+
+  try
+    {
+      // Another duplicate so we can test "finish".
+      next_cases.function2 ();
+    }
+  catch (...)
+    {
+      // Discard
+    }
+
+  // Another test for "finish".
+  try
+    {
+      next_cases.finish ();
+    }
+  catch (...)
+    {
+    }
+
+  // Test of "until".
+  try
+    {
+      next_cases.finish ();
+    }
+  catch (...)
+    {
+    }
+
+  // Test of "until" with an argument.
+  try
+    {
+      next_cases.until ();
+    }
+  catch (...)
+    {
+    }
+
+  // Test of "advance".
+  try
+    {
+      next_cases.until ();
+    }
+  catch (...)
+    {
+    }
+}
+
diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
new file mode 100644
index 0000000..04e9c82
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.exp
@@ -0,0 +1,182 @@
+# Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+if { [skip_cplus_tests] } { continue }
+
+set testfile "gdb9593"
+set srcfile ${testfile}.cc
+set binfile $objdir/$subdir/$testfile
+
+# Create and source the file that provides information about the compiler
+# used to compile the test case.
+if [get_compiler_info ${binfile} "c++"] {
+    untested gdb9593.exp
+    return -1
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+    untested gdb9593.exp
+    return -1
+}
+
+# Some targets can't do function calls, so don't even bother with this
+# test.
+if [target_info exists gdb,cannot_call_functions] {
+    setup_xfail "*-*-*" 9593
+    fail "This target can not call functions"
+    continue
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+    perror "couldn't run to main"
+    continue
+} 
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" {
+    -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+	pass "check for unwinder hook"
+    }
+    -re "No symbol .* in current context.\r\n$gdb_prompt $" {
+	# Pass the test so we don't get bogus fails in the results.
+	pass "check for unwinder hook"
+	set ok 0
+    }
+}
+if {!$ok} {
+    untested gdb9593.exp
+    return -1
+}
+
+# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
+
+gdb_test "next" \
+    ".*catch (...).*" \
+    "next over a throw 1"
+
+gdb_test "next" \
+  ".*next_cases.function2.*" \
+  "next past catch 1"
+
+gdb_test "next" \
+    ".*catch (...).*" \
+    "next over a throw 2"
+
+gdb_test "next" \
+  ".*next_cases.function2.*" \
+  "next past catch 2"
+
+gdb_test "step" \
+  ".*function1().*" \
+  "step into function2 1"
+
+gdb_test "next" \
+    ".*catch (...).*" \
+    "next over a throw 3"
+
+gdb_test "next" \
+  ".*next_cases.function3.*" \
+  "next past catch 3"
+
+gdb_test "next" \
+  ".*next_cases.rethrow.*" \
+    "next over a throw 4"
+
+gdb_test "next" \
+  ".*catch (...).*" \
+  "next over a rethrow"
+
+gdb_test "next" \
+  ".*next_cases.function2.*" \
+  "next after a rethrow"
+
+gdb_test "step" \
+  ".*function1().*" \
+  "step into function2 2"
+
+gdb_test "finish" \
+  ".*catch (...).*" \
+  "finish 1"
+
+gdb_test "next" \
+  ".*next_cases.finish ().*" \
+  "next past catch 4"
+
+gdb_test "step" \
+  ".*function1 ().*" \
+  "step into finish method"
+
+gdb_test "finish" \
+  ".*catch (...).*" \
+  "finish 2"
+
+gdb_test "next" \
+  ".*next_cases.finish ().*" \
+  "next past catch 5"
+
+gdb_test "step" \
+  ".*function1 ().*" \
+  "step into finish, for until"
+
+gdb_test "until" \
+  ".*catch .int x.*" \
+  "until with no argument 1"
+
+set line [gdb_get_line_number "marker for until" $testfile.cc]
+
+gdb_test "until $line" \
+  ".*function1 ().*" \
+  "next past catch 6"
+
+gdb_test "until" \
+  ".*catch (...).*" \
+  "until with no argument 2"
+
+set line [gdb_get_line_number "until here" $testfile.cc]
+
+gdb_test "next" \
+  ".*next_cases.until ().*" \
+  "next past catch 6"
+
+gdb_test "step" \
+  ".*function1 ().*" \
+  "step into until"
+
+gdb_test "until $line" \
+  ".*catch (...).*" \
+  "until-over-throw"
+
+gdb_test "next" \
+  ".*next_cases.until ().*" \
+  "next past catch 7"
+
+gdb_test "step" \
+  ".*function1 ().*" \
+  "step into until, for advance"
+
+gdb_test "advance $line" \
+  ".*catch (...).*" \
+  "advance-over-throw"
diff --git a/gdb/testsuite/gdb.java/jnpe.exp b/gdb/testsuite/gdb.java/jnpe.exp
new file mode 100644
index 0000000..f16c750
--- /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.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set line [gdb_get_line_number "break here" $testfile.java]
+gdb_test "break $testfile.java:$line" ""
+
+gdb_test "run" \
+  "// break here.*" \
+  "run java next-over-throw"
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook in java" {
+    -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+	pass "check for unwinder hook in java"
+    }
+    -re "No symbol .* in current context.?\r\n$gdb_prompt $" {
+	# Pass the test so we don't get bogus fails in the results.
+	setup_xfail *-*-*
+	fail "check for unwinder hook in java"
+	set ok 0
+    }
+}
+if {!$ok} {
+    untested jnpe.exp
+    return -1
+}
+
+gdb_test "handle SIGSEGV nostop noprint" \
+  "SIGSEGV.*fault" \
+  "disable SIGSEGV for next-over-NPE"
+
+# The line where we stop differ according to gcj; check just we did not already
+# execute the catch point.
+
+gdb_test "next" \
+  "" \
+  "next over NPE"
+
+gdb_breakpoint [gdb_get_line_number "catch point"]
+gdb_continue_to_breakpoint "catch point" ".*// catch point.*"
diff --git a/gdb/testsuite/gdb.java/jnpe.java b/gdb/testsuite/gdb.java/jnpe.java
new file mode 100644
index 0000000..3524830
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.java
@@ -0,0 +1,38 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2009 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+   */
+
+public class jnpe
+{
+  public static String npe ()
+  {
+    return ((Object) null).toString();
+  }
+
+  public static void main (String[] args)
+  {
+    try
+      {
+	System.out.println (npe ()); // break here
+      }
+    catch (NullPointerException n)
+      {
+	System.out.println ("success"); // catch point
+      }
+  }
+}

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-10-07  1:37 RFC: next/finish/etc -vs- exceptions Tom Tromey
@ 2010-11-24 17:53 ` Joel Brobecker
  2010-11-24 18:24   ` Tom Tromey
  2010-11-25  7:59 ` Jan Kratochvil
  2010-12-02 15:32 ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Joel Brobecker @ 2010-11-24 17:53 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

> I'd appreciate comments.  I don't want to commit this without a review,
> as my experience is that I usually err when touching breakpoints or
> infrun.

Outch - no feedback as far as I can tell since the patch as posted
something like 6 weeks ago :-(.  We are all so busy these days...
When that happens, I suggest we avoid being too conservative on the HEAD,
especially this far before the next branch creation.  If you've already
had Pedro's comments and you think you addressed them, how about going
ahead? We can always revert later on...

> 2010-10-06  Tom Tromey  <tromey@redhat.com>
> 
> 	* infrun.c (handle_inferior_event): Handle exception breakpoints.
> 	(handle_inferior_event): Likewise.
> 	(insert_exception_resume_breakpoint): New function.
> 	(check_exception_resume): Likewise.
> 	* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
> 	* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
> 	(step_1): Set thread's initiating frame.
> 	(until_next_continuation): New function.
> 	(until_next_command): Support exception breakpoints.
> 	(finish_command_continuation): Delete longjmp breakpoint.
> 	(finish_forward): Support exception breakpoints.
> 	* gdbthread.h (struct thread_info) <initiating_frame>: New field.
> 	* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
> 	bp_exception_master>: New constants.
> 	(struct bpstat_what) <is_longjmp>: New field.
> 	* breakpoint.c (create_exception_master_breakpoint): New function.
> 	(update_breakpoints_after_exec): Handle bp_exception_master.  Call
> 	create_exception_master_breakpoint.
> 	(print_it_typical): Handle bp_exception_master, bp_exception.
> 	(bpstat_stop_status): Handle bp_exception_master.
> 	(bpstat_what): Handle bp_exception_master, bp_exception,
> 	bp_exception_resume.
> 	(bptype_string): Likewise.
> 	(print_one_breakpoint_location): Likewise.
> 	(allocate_bp_location): Likewise.
> 	(set_longjmp_breakpoint): Handle exception breakpoints.
> 	(delete_longjmp_breakpoint): Likewise.
> 	(mention): Likewise.
> 	(struct until_break_command_continuation_args) <thread_num>: New
> 	field.
> 	(until_break_command_continuation): Call
> 	delete_longjmp_breakpoint.
> 	(until_break_command): Support exception breakpoints.
> 	(delete_command): Likewise.
> 	(breakpoint_re_set_one): Likewise.
> 	(breakpoint_re_set): Likewise.
> 
> 2010-10-06  Tom Tromey  <tromey@redhat.com>
> 
> 	* gdb.java/jnpe.java: New file.
> 	* gdb.java/jnpe.exp: New file.
> 	* gdb.cp/gdb9593.exp: New file.
> 	* gdb.cp/gdb9593.cc: New file.

I started reviewing the patch, and quickly realized that this is
flying way over my head, and would require a significant amount
of time for me to really understand...

I did notice a couple of tiny details, though:

> +    case bp_exception_master:
> +      /* These should never be enabled.  */
> +      printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
> +      result = PRINT_NOTHING;
> +      break;

I understand that an assert or internal-error would be unnecessarily
catastrophic for the user, but how about a warning instead? (just a
thought - this is really cosmetic)

> +
> +    /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME.  True if we are
> +       handling a longjmp, false if we are handling an exception.  */
> +    int is_longjmp;

This component is also used with BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.

Sorry for not being of any real help in this case...

-- 
Joel

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-24 17:53 ` Joel Brobecker
@ 2010-11-24 18:24   ` Tom Tromey
  0 siblings, 0 replies; 21+ messages in thread
From: Tom Tromey @ 2010-11-24 18:24 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:

Joel> We are all so busy these days...  When that happens, I suggest we
Joel> avoid being too conservative on the HEAD, especially this far
Joel> before the next branch creation.  If you've already had Pedro's
Joel> comments and you think you addressed them, how about going ahead?
Joel> We can always revert later on...

I will consider it.  If I do it will not be until next week at the
earliest.

Tom> +    case bp_exception_master:
Tom> +      /* These should never be enabled.  */
Tom> +      printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
Tom> +      result = PRINT_NOTHING;
Tom> +      break;

Joel> I understand that an assert or internal-error would be unnecessarily
Joel> catastrophic for the user, but how about a warning instead? (just a
Joel> thought - this is really cosmetic)

I just copied what the other cases in that function do.
Maybe they should be changed -- I am not sure.
If so, that should be a separate patch.

Tom> +    /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME.  True if we are
Tom> +       handling a longjmp, false if we are handling an exception.  */
Tom> +    int is_longjmp;

Joel> This component is also used with BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.

Thanks, I updated my branch.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-10-07  1:37 RFC: next/finish/etc -vs- exceptions Tom Tromey
  2010-11-24 17:53 ` Joel Brobecker
@ 2010-11-25  7:59 ` Jan Kratochvil
  2010-11-27 17:25   ` Doug Evans
                     ` (2 more replies)
  2010-12-02 15:32 ` Tom Tromey
  2 siblings, 3 replies; 21+ messages in thread
From: Jan Kratochvil @ 2010-11-25  7:59 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, 07 Oct 2010 03:37:38 +0200, Tom Tromey wrote:
> 	* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
> 	bp_exception_master>: New constants.

Therefore it is a precent new `bptype's are permitted instead of using
breakpoint_ops (which may need some extensions, not sure).

It may be explained also by the Joel's difficulties to do so:
	http://sourceware.org/ml/gdb-patches/2009-06/msg00298.html

BTW the testcase does not work on neither ppc32 nor on ppc64.  ppc32 due to
some unexpected next/step stop lines.  I see two places in the code using
CORE_ADDR where is probably gdbarch_convert_from_func_ptr_addr appropriate for
the ppc64 case.


> @@ -8524,6 +8588,10 @@ 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 (thread);
> +      tp->initiating_frame = frame_unwind_caller_id (frame);

tp->initiating_frame should be initialized from set_longjmp_breakpoint, as it
is required for that operation.

It probably should not be placed in TP.  If we are stepping/until-ing/etc.
some code and execute some breakpoint's command list trying to step/next/etc.
again already from a different frame it won't work.  But this is a problem for
most of the TP variables already so that's OK for this patch.  It should be
carried over from the set-breakpoint to resume-breakpoint otherwise.


> +static void
> +until_next_continuation (void *arg)
> +{
> +  struct thread_info *tp = arg;

Missing empty line. :-)

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


> @@ -1270,7 +1284,19 @@ until_next_command (int from_tty)
>  
>    tp->step_multi = 0;		/* Only one call to proceed */
>  
> +  set_longjmp_breakpoint (thread);
> +  tp->initiating_frame = get_frame_id (frame);
> +  old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
> +
>    proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
> +
> +  if (target_can_async_p () && is_running (inferior_ptid))
> +    {
> +      discard_cleanups (old_chain);
> +      add_continuation (tp, until_next_continuation, tp, NULL);

continuation_free_args is NULL here but I think the breakpoint should get
deleted even if there is some premature thread deletion.  But maybe just all
the breakpoints specific for that thread (clear_thread_inferior_resources)
should be deleted which would also solve this problem?


> +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)

Shouldn't it be RETURN_MASK_ERROR then?  Otherwise it should rethrow QUIT (I
hope it works that way) but no such cleanups are probably needed here.


> +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)

again RETURN_MASK_ERROR probably.


> diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
> new file mode 100644
> index 0000000..04e9c82
> --- /dev/null
> +++ b/gdb/testsuite/gdb.cp/gdb9593.exp

Please do not use numeric names for testcases.  When dealing with them during
regression testing / rebasing etc. it makes more difficult to keep track of
all the numbers and what they were testing.


> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
> +    untested gdb9593.exp
> +    return -1
> +}

maybe prepare_for_testing?
(nitpick)


> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}

prepare_for_testing / clean_restart
(nitpick)


> +if {!$ok} {
> +    untested gdb9593.exp

rather unsupported?  Maybe not, just a hint.

> +    return -1
> +}
> +
> +# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
> +
> +gdb_test "next" \
> +    ".*catch (...).*" \
> +    "next over a throw 1"

These all have redundant leading `.*'.  `(...)' is not backslashed.


> --- /dev/null
> +++ b/gdb/testsuite/gdb.java/jnpe.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
> +}

maybe prepare_for_testing?
(nitpick)


> +# The line where we stop differ according to gcj; check just we did not already
> +# execute the catch point.
> +
> +gdb_test "next" \
> +  "" \

Here should be ".*" (such sanity checking is missing now in lib/gdb.exp).


> --- /dev/null
> +++ b/gdb/testsuite/gdb.java/jnpe.java
> @@ -0,0 +1,38 @@
> +// Test next-over-NPE.
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2009 Free Software Foundation, Inc.

+2010


> +  public static void main (String[] args)
> +  {
> +    try
> +      {
> +	System.out.println (npe ()); // break here
> +      }
> +    catch (NullPointerException n)
> +      {
> +	System.out.println ("success"); // catch point

BTW the testcase fives on Fedora 14 x86_64 and i686:
+FAIL: gdb.java/jnpe.exp: continue to breakpoint: catch point

but this is due to -O0 -g .debug_line wrong in this case.

Breakpoint for line 35 is at 0x400cb3:
   0x0000000000400cb3 <+112>:	mov    $0x601560,%edi
   0x0000000000400cb8 <+117>:	callq  0x400a18 <_Jv_InitClass@plt>
   0x0000000000400cbd <+122>:	mov    $0x1,%ebx

But the execution goes:
(gdb) stepi
0x0000000000400d0d	33	    catch (NullPointerException n)
2: x/i $pc
=> 0x400d0d <jnpe.main(java.lang.String[])void+202>:	mov    (%rax),%rax
(gdb) stepi
35		System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400d10 <jnpe.main(java.lang.String[])void+205>:	test   %bl,%bl
(gdb) stepi
0x0000000000400d12	35		System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400d12 <jnpe.main(java.lang.String[])void+207>:	
    jne    0x400cbd <jnpe.main(java.lang.String[])void+122>
(gdb) stepi
0x0000000000400cbd	35		System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400cbd <jnpe.main(java.lang.String[])void+122>:	mov    $0x1,%ebx


> +      }
> +  }
> +}


Thanks,
Jan

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-25  7:59 ` Jan Kratochvil
@ 2010-11-27 17:25   ` Doug Evans
  2010-11-28  8:29     ` Joel Brobecker
  2010-11-30 16:43   ` Tom Tromey
  2010-11-30 18:23   ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Doug Evans @ 2010-11-27 17:25 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Tom Tromey, gdb-patches

On Wed, Nov 24, 2010 at 11:58 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
>> diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
>> new file mode 100644
>> index 0000000..04e9c82
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.cp/gdb9593.exp
>
> Please do not use numeric names for testcases.  When dealing with them during
> regression testing / rebasing etc. it makes more difficult to keep track of
> all the numbers and what they were testing.

For reference sake,
if this is to become a rule, I think we should codify it in the coding
standards.

Personally, I don't mind the numbers, but I'm happy to go with the flow.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-27 17:25   ` Doug Evans
@ 2010-11-28  8:29     ` Joel Brobecker
  0 siblings, 0 replies; 21+ messages in thread
From: Joel Brobecker @ 2010-11-28  8:29 UTC (permalink / raw)
  To: Doug Evans; +Cc: Jan Kratochvil, Tom Tromey, gdb-patches

> For reference sake,
> if this is to become a rule, I think we should codify it in the coding
> standards.
> 
> Personally, I don't mind the numbers, but I'm happy to go with the flow.

My two cents:

To me, the numbers mean nothing. They are useful to get more background
info on the original problem, but a comment in the script would also
work.  On the other hand, when searching testcases that test a certain
feature/scenario, more descriptive names often help.

-- 
Joel

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-25  7:59 ` Jan Kratochvil
  2010-11-27 17:25   ` Doug Evans
@ 2010-11-30 16:43   ` Tom Tromey
  2010-11-30 17:02     ` Jan Kratochvil
                       ` (2 more replies)
  2010-11-30 18:23   ` Tom Tromey
  2 siblings, 3 replies; 21+ messages in thread
From: Tom Tromey @ 2010-11-30 16:43 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Thanks for the review.

I've made most of the changes you recommended.

>> * breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
>> bp_exception_master> : New constants.

Jan> Therefore it is a precent new `bptype's are permitted instead of using
Jan> breakpoint_ops (which may need some extensions, not sure).

Yeah.  Mostly I just copied and then extended the existing longjmp
support.

I looked briefly at using breakpoint_ops, but it seems like it would
require a bunch of new methods that are specific to just this
breakpoint.  Maybe that is the way to go?

Jan> BTW the testcase does not work on neither ppc32 nor on ppc64.

Thanks, I will investigate.

Jan> tp-> initiating_frame should be initialized from
Jan> set_longjmp_breakpoint, as it is required for that operation.

I made this change.

Jan> It probably should not be placed in TP.  If we are
Jan> stepping/until-ing/etc.  some code and execute some breakpoint's
Jan> command list trying to step/next/etc.  again already from a
Jan> different frame it won't work.  But this is a problem for most of
Jan> the TP variables already so that's OK for this patch.  It should be
Jan> carried over from the set-breakpoint to resume-breakpoint
Jan> otherwise.

I thought that gdb did not support nested inferior-control commands like
this.

It would be a nice feature.

>> +      add_continuation (tp, until_next_continuation, tp, NULL);

Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?

I will try to make a test case so I can see what actually happens in
this scenario.  Thanks for pointing it out.

>> +if  { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
>> +    untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
>> +    return -1

Jan> maybe prepare_for_testing?
Jan> (nitpick)

I left this as-is.  compile_java_from_source does some extra processing
right now.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 16:43   ` Tom Tromey
@ 2010-11-30 17:02     ` Jan Kratochvil
  2010-11-30 17:15       ` Phil Muldoon
  2010-11-30 20:15     ` Tom Tromey
  2010-12-01 21:40     ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Jan Kratochvil @ 2010-11-30 17:02 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches, Phil Muldoon

On Tue, 30 Nov 2010 17:43:28 +0100, Tom Tromey wrote:
> I looked briefly at using breakpoint_ops, but it seems like it would
> require a bunch of new methods that are specific to just this
> breakpoint.  Maybe that is the way to go?

One day probably.


> Jan> It probably should not be placed in TP.  If we are
> Jan> stepping/until-ing/etc.  some code and execute some breakpoint's
> Jan> command list trying to step/next/etc.  again already from a
> Jan> different frame it won't work.  But this is a problem for most of
> Jan> the TP variables already so that's OK for this patch.  It should be
> Jan> carried over from the set-breakpoint to resume-breakpoint
> Jan> otherwise.
> 
> I thought that gdb did not support nested inferior-control commands like
> this.
> 
> It would be a nice feature.

I was thinking Phil was coding something like that with Python commands
attached to breakpoints.  It would need more fixes but I was just pointing
out an incompatibility also in this code.

With current GDB it seems OK to me.


> >> +if  { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
> >> +    untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
> >> +    return -1
> 
> Jan> maybe prepare_for_testing?
> Jan> (nitpick)
> 
> I left this as-is.  compile_java_from_source does some extra processing
> right now.

I see now, sorry.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 17:02     ` Jan Kratochvil
@ 2010-11-30 17:15       ` Phil Muldoon
  0 siblings, 0 replies; 21+ messages in thread
From: Phil Muldoon @ 2010-11-30 17:15 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: Tom Tromey, gdb-patches

Jan Kratochvil <jan.kratochvil@redhat.com> writes:

>> I thought that gdb did not support nested inferior-control commands like
>> this.
>> 
>> It would be a nice feature.
>
> I was thinking Phil was coding something like that with Python commands
> attached to breakpoints.  It would need more fixes but I was just pointing
> out an incompatibility also in this code.
>
> With current GDB it seems OK to me.

Conditional breakpoints in Python that evaluate a Python function to
determine whether or not to stop.  Though there is no reason why in the
future what you describe could not happen with commands attached to a
breakpoint.  

Cheers,

Phil

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-25  7:59 ` Jan Kratochvil
  2010-11-27 17:25   ` Doug Evans
  2010-11-30 16:43   ` Tom Tromey
@ 2010-11-30 18:23   ` Tom Tromey
  2010-11-30 18:55     ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-11-30 18:23 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?

It turns out there is a bug here in CVS gdb, with longjmp breakpoints.

Consider the appended program.  Set a breakpoint in thr_func, run, then
"next".  I get:

(gdb) n
Warning:
Cannot insert breakpoint 0.
Error accessing memory address 0x83f4375: Input/output error.

0x00757b01 in __libc_siglongjmp (env=0xb7fd8444, val=1) at longjmp.c:30
30	{


I'm looking into it some more.

Tom


#include <pthread.h>

void throwit () {
  // throw 1;
  pthread_exit ((void*) "lo");
}

void *
thr_func (void *)
{
  try {
    throwit ();
  }
  catch (int x) {
    return (void*) "hi bob";
  }

  return NULL;
}

int main ()
{
  pthread_t thread;
  void *r;

  pthread_create (&thread, NULL, thr_func, NULL);

  pthread_join (thread, &r);
}

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 18:23   ` Tom Tromey
@ 2010-11-30 18:55     ` Tom Tromey
  0 siblings, 0 replies; 21+ messages in thread
From: Tom Tromey @ 2010-11-30 18:55 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

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

Tom> It turns out there is a bug here in CVS gdb, with longjmp breakpoints.

Tom> Error accessing memory address 0x83f4375: Input/output error.

Tom> I'm looking into it some more.

Oh, duh.  This happens because the gdb can't decode the mangled PC in
glibc's jmp_buf, aka http://sourceware.org/bugzilla/show_bug.cgi?id=9270

I'm going to hack around this so I can test my patch.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 16:43   ` Tom Tromey
  2010-11-30 17:02     ` Jan Kratochvil
@ 2010-11-30 20:15     ` Tom Tromey
  2010-12-01 13:42       ` Jan Kratochvil
  2010-12-01 21:40     ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-11-30 20:15 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

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

>>> +      add_continuation (tp, until_next_continuation, tp, NULL);

Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?

Tom> I will try to make a test case so I can see what actually happens in
Tom> this scenario.  Thanks for pointing it out.

Good catch here, we were leaving around the exception breakpoints.
Calling delete_longjmp_breakpoint from clear_thread_inferior_resources
worked for me.  I'm not 100% confident that this is the correct fix
though.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 20:15     ` Tom Tromey
@ 2010-12-01 13:42       ` Jan Kratochvil
  0 siblings, 0 replies; 21+ messages in thread
From: Jan Kratochvil @ 2010-12-01 13:42 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Tue, 30 Nov 2010 21:15:26 +0100, Tom Tromey wrote:
> Jan> But maybe just all the breakpoints specific for that thread
> Jan> (clear_thread_inferior_resources) should be deleted which would
> Jan> also solve this problem?

Found out now I already filed it as:
	delete thread-specific breakpoint on the thread exit
	http://sourceware.org/bugzilla/show_bug.cgi?id=11568


> Calling delete_longjmp_breakpoint from clear_thread_inferior_resources
> worked for me.  I'm not 100% confident that this is the correct fix though.

It is also not much a serious problem.


Thanks,
Jan

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-11-30 16:43   ` Tom Tromey
  2010-11-30 17:02     ` Jan Kratochvil
  2010-11-30 20:15     ` Tom Tromey
@ 2010-12-01 21:40     ` Tom Tromey
  2 siblings, 0 replies; 21+ messages in thread
From: Tom Tromey @ 2010-12-01 21:40 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

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

Jan> BTW the testcase does not work on neither ppc32 nor on ppc64.

Tom> Thanks, I will investigate.

On PPC32 the problem was just that the test case was not robust against
differences between compilers on different arches.  In particular,
different versions of gcc seem to give different (sometimes even quite
weird) line numbers to the landing point of an exception.

I fixed this by rewriting the test case to be more robust against these
kinds of differences.


On PPC64, as you surmised, the problem was related to function
descriptors.  In particular, create_exception_master_breakpoint
was calling lookup_minimal_symbol_text -- but that failed on PPC64.
FWIW I expect that the longjmp code also has a problem here, but again
probably masked by glibc PC mangling.


I think I've addressed all the comments.  I'll send an updated patch
tomorrow.

Tom

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-10-07  1:37 RFC: next/finish/etc -vs- exceptions Tom Tromey
  2010-11-24 17:53 ` Joel Brobecker
  2010-11-25  7:59 ` Jan Kratochvil
@ 2010-12-02 15:32 ` Tom Tromey
  2010-12-09 16:37   ` Tom Tromey
  2 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-12-02 15:32 UTC (permalink / raw)
  To: gdb-patches

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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-12-02 15:32 ` Tom Tromey
@ 2010-12-09 16:37   ` Tom Tromey
  2010-12-10  4:52     ` Jan Kratochvil
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-12-09 16:37 UTC (permalink / raw)
  To: gdb-patches

>>>>> "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

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-12-09 16:37   ` Tom Tromey
@ 2010-12-10  4:52     ` Jan Kratochvil
  2010-12-10 20:07       ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Jan Kratochvil @ 2010-12-10  4:52 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Thu, 09 Dec 2010 17:37:17 +0100, Tom Tromey wrote:
> >>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
> I rebased this patch on CVS HEAD and committed it.
> I'm appending the updated version.

The attached testcase extension causes:

breakpoint.c:4393: internal-error: bpstat_what: unhandled bptype -1785358955

The new field `tp->control.exception_resume_breakpoint' should be probably
handled by existing save_infcall_control_state, restore_infcall_control_state
and discard_infcall_control_state somehow like step_resume_breakpoint is.


Thanks,
Jan


--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
 
 using namespace std;
 
-void dummy ()
+int dummy ()
 {
+  return 0;
 }
 
 class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
     function1 (val);		// until here
   }
 
+  void resumebpt (int val)
+  {
+    try
+      {
+	throw val;
+      }
+    catch (int x)
+      {
+	dummy ();
+      }
+  }
+
 };
 NextOverThrowDerivates next_cases;
 
@@ -198,6 +211,18 @@ int main ()
       testval = val;		// End: advance
     }
 
+  // Test of "resumebpt".
+  try
+    {
+      next_cases.resumebpt (10);	// Start: resumebpt
+      next_cases.resumebpt (11);	// Second: resumebpt
+    }
+  catch (int val)
+    {
+      dummy ();
+      testval = val;		// End: resumebpt
+    }
+
   testval = 32;			// done
 }
 
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
 tbreak_and_cont "End: advance"
 verify_testval "pre-check - advance" 8
 
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
 tbreak_and_cont "done"
 verify_testval "post-check - advance" 9

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-12-10  4:52     ` Jan Kratochvil
@ 2010-12-10 20:07       ` Tom Tromey
  2010-12-11  5:27         ` Jan Kratochvil
  0 siblings, 1 reply; 21+ messages in thread
From: Tom Tromey @ 2010-12-10 20:07 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Jan> The new field `tp->control.exception_resume_breakpoint' should be
Jan> probably handled by existing save_infcall_control_state,
Jan> restore_infcall_control_state and discard_infcall_control_state
Jan> somehow like step_resume_breakpoint is.

Thanks for the test case.

Here is what I am testing.

It just mirrors the existing step_resume_breakpoint code.
I think that this is the right thing to do -- stash any
exception-related breakpoint while doing the infcall, then restore it
if the infcall succeeds.

Tom

diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1bc00a4..f72a18d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6379,6 +6379,7 @@ save_infcall_control_state (void)
   inf_status->inferior_control = inf->control;
 
   tp->control.step_resume_breakpoint = NULL;
+  tp->control.exception_resume_breakpoint = NULL;
 
   /* Save original bpstat chain to INF_STATUS; replace it in TP with copy of
      chain.  If caller's caller is walking the chain, they'll be happier if we
@@ -6428,6 +6429,10 @@ restore_infcall_control_state (struct infcall_control_state *inf_status)
   if (tp->control.step_resume_breakpoint)
     tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
 
+  if (tp->control.exception_resume_breakpoint)
+    tp->control.exception_resume_breakpoint->disposition
+      = disp_del_at_next_stop;
+
   /* Handle the bpstat_copy of the chain.  */
   bpstat_clear (&tp->control.stop_bpstat);
 
@@ -6476,6 +6481,10 @@ discard_infcall_control_state (struct infcall_control_state *inf_status)
     inf_status->thread_control.step_resume_breakpoint->disposition
       = disp_del_at_next_stop;
 
+  if (inf_status->thread_control.exception_resume_breakpoint)
+    inf_status->thread_control.exception_resume_breakpoint->disposition
+      = disp_del_at_next_stop;
+
   /* See save_infcall_control_state for info on stop_bpstat. */
   bpstat_clear (&inf_status->thread_control.stop_bpstat);
 
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.cc b/gdb/testsuite/gdb.cp/nextoverthrow.cc
index b25cb34..c3da516 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
 
 using namespace std;
 
-void dummy ()
+int dummy ()
 {
+  return 0;
 }
 
 class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
     function1 (val);		// until here
   }
 
+  void resumebpt (int val)
+  {
+    try
+      {
+	throw val;
+      }
+    catch (int x)
+      {
+	dummy ();
+      }
+  }
+
 };
 NextOverThrowDerivates next_cases;
 
@@ -198,6 +211,18 @@ int main ()
       testval = val;		// End: advance
     }
 
+  // Test of "resumebpt".
+  try
+    {
+      next_cases.resumebpt (10);	// Start: resumebpt
+      next_cases.resumebpt (11);	// Second: resumebpt
+    }
+  catch (int val)
+    {
+      dummy ();
+      testval = val;		// End: resumebpt
+    }
+
   testval = 32;			// done
 }
 
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
index 960ea0d..c19674f 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
 tbreak_and_cont "End: advance"
 verify_testval "pre-check - advance" 8
 
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
 tbreak_and_cont "done"
 verify_testval "post-check - advance" 9

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-12-10 20:07       ` Tom Tromey
@ 2010-12-11  5:27         ` Jan Kratochvil
  2010-12-15 21:18           ` Tom Tromey
  0 siblings, 1 reply; 21+ messages in thread
From: Jan Kratochvil @ 2010-12-11  5:27 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On Fri, 10 Dec 2010 21:07:02 +0100, Tom Tromey wrote:
> >>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
> Here is what I am testing.

OK.  Included a ChangeLog entry.


Thanks,
Jan


gdb.testsuite/
2010-12-11  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.cp/nextoverthrow.cc (dummy): Return int 0.
	(NextOverThrowDerivates) <resumebpt>: New.
	(main): New block for test of "resumebpt".
	* gdb.cp/nextoverthrow.exp: New block for test of "resumebpt".

--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
 
 using namespace std;
 
-void dummy ()
+int dummy ()
 {
+  return 0;
 }
 
 class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
     function1 (val);		// until here
   }
 
+  void resumebpt (int val)
+  {
+    try
+      {
+	throw val;
+      }
+    catch (int x)
+      {
+	dummy ();
+      }
+  }
+
 };
 NextOverThrowDerivates next_cases;
 
@@ -198,6 +211,18 @@ int main ()
       testval = val;		// End: advance
     }
 
+  // Test of "resumebpt".
+  try
+    {
+      next_cases.resumebpt (10);	// Start: resumebpt
+      next_cases.resumebpt (11);	// Second: resumebpt
+    }
+  catch (int val)
+    {
+      dummy ();
+      testval = val;		// End: resumebpt
+    }
+
   testval = 32;			// done
 }
 
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
index 960ea0d..c19674f 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
 tbreak_and_cont "End: advance"
 verify_testval "pre-check - advance" 8
 
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
 tbreak_and_cont "done"
 verify_testval "post-check - advance" 9

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
  2010-12-11  5:27         ` Jan Kratochvil
@ 2010-12-15 21:18           ` Tom Tromey
  0 siblings, 0 replies; 21+ messages in thread
From: Tom Tromey @ 2010-12-15 21:18 UTC (permalink / raw)
  To: Jan Kratochvil; +Cc: gdb-patches

>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:

Tom> Here is what I am testing.

Jan> OK.  Included a ChangeLog entry.

Thanks.

I had to modify the test a little to make it pass with gcc svn trunk.
It seems the test was a bit confused about the line number it was on.

Here is what I am checking in.

Tom

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

	* infrun.c (save_infcall_control_state): Handle the
	exception-resume breakpoint.
	(restore_infcall_control_state): Likewise.
	(discard_infcall_control_state): Likewise.

2010-12-15  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdb.cp/nextoverthrow.cc (dummy): Return int 0.
	(NextOverThrowDerivates) <resumebpt>: New.
	(resumebpt_test): New.
	(main): Call resumebpt_test.
	* gdb.cp/nextoverthrow.exp: New block for test of "resumebpt".

Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.461
diff -u -r1.461 infrun.c
--- infrun.c	9 Dec 2010 16:09:52 -0000	1.461
+++ infrun.c	15 Dec 2010 21:15:45 -0000
@@ -6379,6 +6379,7 @@
   inf_status->inferior_control = inf->control;
 
   tp->control.step_resume_breakpoint = NULL;
+  tp->control.exception_resume_breakpoint = NULL;
 
   /* Save original bpstat chain to INF_STATUS; replace it in TP with copy of
      chain.  If caller's caller is walking the chain, they'll be happier if we
@@ -6428,6 +6429,10 @@
   if (tp->control.step_resume_breakpoint)
     tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
 
+  if (tp->control.exception_resume_breakpoint)
+    tp->control.exception_resume_breakpoint->disposition
+      = disp_del_at_next_stop;
+
   /* Handle the bpstat_copy of the chain.  */
   bpstat_clear (&tp->control.stop_bpstat);
 
@@ -6476,6 +6481,10 @@
     inf_status->thread_control.step_resume_breakpoint->disposition
       = disp_del_at_next_stop;
 
+  if (inf_status->thread_control.exception_resume_breakpoint)
+    inf_status->thread_control.exception_resume_breakpoint->disposition
+      = disp_del_at_next_stop;
+
   /* See save_infcall_control_state for info on stop_bpstat. */
   bpstat_clear (&inf_status->thread_control.stop_bpstat);
 
Index: testsuite/gdb.cp/nextoverthrow.cc
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.cp/nextoverthrow.cc,v
retrieving revision 1.1
diff -u -r1.1 nextoverthrow.cc
--- testsuite/gdb.cp/nextoverthrow.cc	9 Dec 2010 16:09:53 -0000	1.1
+++ testsuite/gdb.cp/nextoverthrow.cc	15 Dec 2010 21:15:47 -0000
@@ -19,8 +19,9 @@
 
 using namespace std;
 
-void dummy ()
+int dummy ()
 {
+  return 0;
 }
 
 class NextOverThrowDerivates
@@ -89,10 +90,39 @@
     function1 (val);		// until here
   }
 
+  void resumebpt (int val)
+  {
+    try
+      {
+	throw val;
+      }
+    catch (int x)
+      {
+	dummy ();
+      }
+  }
+
 };
 NextOverThrowDerivates next_cases;
 
 
+int
+resumebpt_test (int x)
+{
+  try
+    {
+      next_cases.resumebpt (x);	    // Start: resumebpt
+      next_cases.resumebpt (x + 1); // Second: resumebpt
+    }
+  catch (int val)
+    {
+      dummy ();
+      x = val;
+    }
+
+  return x;
+}
+
 int main () 
 { 
   int testval = -1;
@@ -198,6 +228,8 @@
       testval = val;		// End: advance
     }
 
+  // Test of "resumebpt".
+  testval = resumebpt_test (10);
+
   testval = 32;			// done
 }
-
Index: testsuite/gdb.cp/nextoverthrow.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.cp/nextoverthrow.exp,v
retrieving revision 1.1
diff -u -r1.1 nextoverthrow.exp
--- testsuite/gdb.cp/nextoverthrow.exp	9 Dec 2010 16:09:53 -0000	1.1
+++ testsuite/gdb.cp/nextoverthrow.exp	15 Dec 2010 21:15:47 -0000
@@ -149,5 +149,14 @@
 tbreak_and_cont "End: advance"
 verify_testval "pre-check - advance" 8
 
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
 tbreak_and_cont "done"
-verify_testval "post-check - advance" 9
+verify_testval "post-check - advance" 10

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: RFC: next/finish/etc -vs- exceptions
       [not found]       ` <20090610171328.GA32661@caradoc.them.org>
@ 2010-11-25  4:54         ` Jan Kratochvil
  0 siblings, 0 replies; 21+ messages in thread
From: Jan Kratochvil @ 2010-11-25  4:54 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: Pedro Alves, gdb-patches, tromey, Joel Brobecker

On Wed, 10 Jun 2009 19:13:28 +0200, Daniel Jacobowitz wrote:
[...]
> exceptions can be thrown through
> signal handlers on many platforms; so yes, they might switch stack.

On Fedora 14 x86_64 getting instead of a caught exception-from-sighandler:

terminate called after throwing an instance of 'int'

from the testcase below and ISO C++ says:

	The common subset of the C and C++ languages consists of all
	declarations, definitions, and expressions that may appear in a well
	formed C++ program and also in a conforming C program. A POF (“plain
	old function”) is a function that uses only features from this common
	subset, and that does not directly or indirectly use any function that
	is not a POF, except that it may use functions defined in Clause 29
	that are not member functions. All signal handlers shall have
	C linkage. A POF that could be used as a signal handler in
	a conforming C program does not produce undefined behavior when used
	as a signal handler in a C++ program. The behavior of any other
	function used as a signal handler in a C++ program is
	implementation-defined.228

	228) In particular, a signal handler using exception handling is very
	                    -------------------------------------------------
	likely to have problems. Also, invoking std::exit may cause
	-----------------------
	destruction of objects, including those of the standard library
	implementation, which, in general, yields undefined behavior in
	a signal handler (see 1.9).

If this really does not have to work it means for GDB the sigaltstack case is
unrelated to this "next/finish/etc -vs- exceptions" patch.


Thanks,
Jan


#include <signal.h>
#include <assert.h>
#include <iostream>
using namespace std;

static void
handler (int signo)
{
  throw 1;
}

int
main (void)
{
  sighandler_t sigvar;

  sigvar = signal (SIGUSR1, handler);
  assert (sigvar == SIG_DFL);

  try
    {
      int i;

      i = raise (SIGUSR1);
      assert (i == 0);
    }
  catch (...)
    {
      cout << "caught" << endl;
    }
  cout << "done" << endl;
}

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2010-12-15 21:18 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-10-07  1:37 RFC: next/finish/etc -vs- exceptions Tom Tromey
2010-11-24 17:53 ` Joel Brobecker
2010-11-24 18:24   ` Tom Tromey
2010-11-25  7:59 ` Jan Kratochvil
2010-11-27 17:25   ` Doug Evans
2010-11-28  8:29     ` Joel Brobecker
2010-11-30 16:43   ` Tom Tromey
2010-11-30 17:02     ` Jan Kratochvil
2010-11-30 17:15       ` Phil Muldoon
2010-11-30 20:15     ` Tom Tromey
2010-12-01 13:42       ` Jan Kratochvil
2010-12-01 21:40     ` Tom Tromey
2010-11-30 18:23   ` Tom Tromey
2010-11-30 18:55     ` Tom Tromey
2010-12-02 15:32 ` Tom Tromey
2010-12-09 16:37   ` Tom Tromey
2010-12-10  4:52     ` Jan Kratochvil
2010-12-10 20:07       ` Tom Tromey
2010-12-11  5:27         ` Jan Kratochvil
2010-12-15 21:18           ` Tom Tromey
     [not found] <m37hzzzgk7.fsf@fleche.redhat.com>
     [not found] ` <20090610161204.GB25703@adacore.com>
     [not found]   ` <m3hbyoghit.fsf@fleche.redhat.com>
     [not found]     ` <200906101806.31977.pedro@codesourcery.com>
     [not found]       ` <20090610171328.GA32661@caradoc.them.org>
2010-11-25  4:54         ` Jan Kratochvil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).