This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [RFC] Python Finish Breakpoints


On Wed, Jan 4, 2012 at 6:12 PM, Ulrich Weigand <uweigand@de.ibm.com> wrote:
> Tom Tromey wrote:
>
>> We have support for "finish", and other operations in the presence of
>> exceptions. ?Search for _Unwind_DebugHook (in breakpoint.c and infrun.c).
>> This code only works if you are using a new-enough version of GCC and
>> have debuginfo for libgcc (but once the stap probe patches are in, you
>> will no longer need debuginfo).
>
> Ah, I see -- that's what I was missing. ?Yes, this looks like the
> way to handle this situation ...

Hello,

I would like to discuss this patch which intends to fix and improve
the handling for c++ exceptions with the Python FinishBreakpoint.

Based on what I understood of the existing mechanisms which handle
longjmps and and exception, I extended the "momentary breakpoints"
with a "nostop" flag. Indeed, their initial behavior/goal was to stop
unconditionally the execution, whereas I just wanted to receive a
notification when the relevant code location was hit (namely
infrun.c::BPSTAT_WHAT_CLEAR_LONGJMP_RESUME). The nostop flag is
propagated step by step (bp by bp) from the exception throw to its
catchpoint, where I check if the FinishBreakpoint have ran out of
their scope.

As Tom mentioned, c++ debug info are needed for this machinery to work
(otherwise, FinishBreakpoint will fail the same way GDB fails: stop at
the beginning of the catch(), never stop, ...) -- I used xfail in the
testsuite to refer to this situation, I'm not sure that's correct).

I've also fixed a bug which triggered twice the out_of_scope
notification in case of inferior exit (observer_attach_normal_stop and
observer_attach_inferior_exit are both triggered when the inferior
exits)

(This is just a preliminary patch, I'll would like to prepare some
more tests with exceptions and longjmps)

Tested with no regression on f15/X86_64

( these always fail on my system:
 Running PATH/git/gdb/gdb/testsuite/gdb.base/longjmp.exp ...
 FAIL: gdb.base/longjmp.exp: next over longjmp(1)
 FAIL: gdb.base/longjmp.exp: next over call_longjmp (2)
 FAIL: gdb.base/longjmp.exp: next over patt3
)

Thanks for your comments,

Kevin

--

2012-01-09  Kevin Pouget <kevin.pouget@st.com>

	* breakpoint.c (momentary_nostop_breakpoint_ops): New variable.
	(bpstat_what): Set field retval.is_nostop when necessary.
	(set_longjmp_breakpoint): Add parameter nostop and create nostop
	momentary breakpoint ops if necessary.
	(set_momentary_breakpoint): Likewise.
	(set_momentary_breakpoint_at_pc): Likewise.
	(momentary_breakpoint_is_nostop): New function.
	(until_break_command): Call set_momentary_breakpoint and
	set_longjmp_breakpoint with nostop not set.
	(momentary_stop_bkpt_check_status): Rename from
	momentary_bkpt_check_status.
	(momentary_bkpt_check_status): Remove.
	(momentary_nostop_bkpt_check_status): New function.
	(initialize_breakpoint_ops): Initialize
	momentary_breakpoint_ops.check_status to
	momentary_stop_bkpt_check_status, initialize
	momentary_nostop_breakpoint_ops.
	* breakpoint.h (struct bpstat_what): New field: is_nostop.
	(set_momentary_breakpoint): New parameter: nostop.
	(set_momentary_breakpoint_at_pc): Liklewise.
	(set_longjmp_breakpoint): Likewise.
	(momentary_breakpoint_is_nostop): New prototype.
	* arm-linux-tdep.c (arm_linux_copy_svc): Add nostop parameter to
        set_momentary_breakpoint call.
	* elfread.c: (elf_gnu_ifunc_resolver_stop): Likewise
	* infcall.c (call_function_by_hand): Likewise.
	* infcmd.c: (finish_forward): Likewise, add nostop parameter to
        set_longjmp_breakpoint call.
	(step_1): Add nostop parameter to set_longjmp_breakpoint call.
	(until_next_command): Likewise.
	* infrun.c: Include python/python.h.
	(insert_step_resume_breakpoint_at_sal_1): Add nostop parameter to
        set_momentary_breakpoint call.
	(insert_longjmp_resume_breakpoint): Add nostop parameter to prototype.
	Add nostop parameter to set_momentary_breakpoint_at_pc call.
	(insert_exception_resume_breakpoint): Likewise.
	(check_exception_resume): Add nostop parameter to prototype. Add nostop
        parameter to insert_exception_resume_breakpoint call.
	(process_event_stop_test): Set and use nostop to prevent GDB from
	stopping the execution, trigger gdbpy_bpfinish_handle_exception when
	necessary.
	* python/py-finishbreakpoint.c (bpfinishpy_init): Call
	set_longjmp_breakpoint.
	(bpfinishpy_detect_out_scope): Renamed from
	bpfinishpy_detect_out_scope_cb. Cleaned up. Add inc_current parameter.
	(bpfinishpy_detect_out_scope_cb): Removed.
	(bpfinishpy_detect_out_scope_bp_cb): New function.
	(bpfinishpy_detect_out_scope_exception_cb): New function.
	(bpfinishpy_handle_stop): Update with function name
	bpfinishpy_detect_out_scope_bp_cb.
	(bpfinishpy_handle_exit): Removed.
	(gdbpy_bpfinish_handle_exception): New function.
	(gdbpy_initialize_finishbreakpoints): Remove call to
	bpfinishpy_handle_exit.
	* python/python.c (gdbpy_bpfinish_handle_exception): New function.
	* python/python.h (gdbpy_bpfinish_handle_exception): New prototype.

testsuite/
	* gdb.python/py-finish-breakpoint2.exp: Strengthen the exception tests.
From 75cd8a288787550a58c9dd3a7db1b5b0c99c587f Mon Sep 17 00:00:00 2001
From: Kevin Pouget <kevin.pouget@st.com>
Date: Fri, 6 Jan 2012 14:14:49 +0100
Subject: [PATCH] better exception support

---
 gdb/arm-linux-tdep.c                               |    2 +-
 gdb/breakpoint.c                                   |   68 +++++++++++----
 gdb/breakpoint.h                                   |   13 ++-
 gdb/elfread.c                                      |    3 +-
 gdb/infcall.c                                      |    3 +-
 gdb/infcmd.c                                       |    9 +-
 gdb/infrun.c                                       |   46 +++++++---
 gdb/python/py-finishbreakpoint.c                   |   96 +++++++++++++-------
 gdb/python/python.c                                |    6 ++
 gdb/python/python.h                                |    2 +
 gdb/testsuite/gdb.python/py-finish-breakpoint2.exp |   22 ++++-
 11 files changed, 194 insertions(+), 76 deletions(-)

diff --git a/gdb/arm-linux-tdep.c b/gdb/arm-linux-tdep.c
index ac4860c..8479839 100644
--- a/gdb/arm-linux-tdep.c
+++ b/gdb/arm-linux-tdep.c
@@ -935,7 +935,7 @@ arm_linux_copy_svc (struct gdbarch *gdbarch, struct regcache *regs,
 	    {
 	      inferior_thread ()->control.step_resume_breakpoint
         	= set_momentary_breakpoint (gdbarch, sal, get_frame_id (frame),
-					    bp_step_resume);
+					    bp_step_resume, /* nostop */ 0);
 
 	      /* We need to make sure we actually insert the momentary
 	         breakpoint set above.  */
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 6bcedc4..e578aed 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -257,6 +257,7 @@ static struct breakpoint_ops internal_breakpoint_ops;
 
 /* Momentary breakpoints class type.  */
 static struct breakpoint_ops momentary_breakpoint_ops;
+static struct breakpoint_ops momentary_nostop_breakpoint_ops;
 
 /* The breakpoint_ops structure to be used in regular user created
    breakpoints.  */
@@ -4308,6 +4309,7 @@ bpstat_what (bpstat bs_head)
   retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
   retval.call_dummy = STOP_NONE;
   retval.is_longjmp = 0;
+  retval.is_nostop = 0;
 
   for (bs = bs_head; bs != NULL; bs = bs->next)
     {
@@ -4364,6 +4366,8 @@ bpstat_what (bpstat bs_head)
 	case bp_exception:
 	  this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
 	  retval.is_longjmp = bptype == bp_longjmp;
+	  retval.is_nostop =
+	      momentary_breakpoint_is_nostop (bs->breakpoint_at);
 	  break;
 	case bp_longjmp_resume:
 	case bp_exception_resume:
@@ -5902,13 +5906,18 @@ make_breakpoint_permanent (struct breakpoint *b)
 
 /* Call this routine when stepping and nexting to enable a breakpoint
    if we do a longjmp() or 'throw' in TP.  FRAME is the frame which
-   initiated the operation.  */
+   initiated the operation.  If the flag NOSTOP is set, the execution will
+   not be stopped upon hitting the breakpoint, but internal functions can be
+   triggered.  */
 
 void
-set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame,
+                        int nostop)
 {
   struct breakpoint *b, *b_tmp;
   int thread = tp->num;
+  struct breakpoint_ops *ops = (nostop ? &momentary_nostop_breakpoint_ops
+                                       : &momentary_breakpoint_ops);
 
   /* To avoid having to rescan all objfile symbols at every step,
      we maintain a list of continually-inserted but always disabled
@@ -5922,8 +5931,7 @@ set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
 	enum bptype type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
 	struct breakpoint *clone;
 
-	clone = momentary_breakpoint_from_master (b, type,
-						  &momentary_breakpoint_ops);
+	clone = momentary_breakpoint_from_master (b, type, ops);
 	clone->thread = thread;
       }
 
@@ -7045,21 +7053,24 @@ enable_breakpoints_after_startup (void)
 }
 
 
-/* Set a breakpoint that will evaporate an end of command
-   at address specified by SAL.
-   Restrict it to frame FRAME if FRAME is nonzero.  */
+/* Set a breakpoint that will evaporate an end of command at address specified
+   by SAL. Restrict it to frame FRAME if FRAME is nonzero.  Will not request
+   execution stop if NOSTOP is set.  */
 
 struct breakpoint *
 set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
-			  struct frame_id frame_id, enum bptype type)
+			  struct frame_id frame_id, enum bptype type,
+			  int nostop)
 {
   struct breakpoint *b;
+  struct breakpoint_ops *ops = (nostop ? &momentary_nostop_breakpoint_ops
+                                       : &momentary_breakpoint_ops);
 
   /* If FRAME_ID is valid, it should be a real frame, not an inlined
      one.  */
   gdb_assert (!frame_id_inlined_p (frame_id));
 
-  b = set_raw_breakpoint (gdbarch, sal, type, &momentary_breakpoint_ops);
+  b = set_raw_breakpoint (gdbarch, sal, type, ops);
   b->enable_state = bp_enabled;
   b->disposition = disp_donttouch;
   b->frame_id = frame_id;
@@ -7127,7 +7138,7 @@ clone_momentary_breakpoint (struct breakpoint *orig)
 
 struct breakpoint *
 set_momentary_breakpoint_at_pc (struct gdbarch *gdbarch, CORE_ADDR pc,
-				enum bptype type)
+				enum bptype type, int nostop)
 {
   struct symtab_and_line sal;
 
@@ -7136,7 +7147,16 @@ set_momentary_breakpoint_at_pc (struct gdbarch *gdbarch, CORE_ADDR pc,
   sal.section = find_pc_overlay (pc);
   sal.explicit_pc = 1;
 
-  return set_momentary_breakpoint (gdbarch, sal, null_frame_id, type);
+  return set_momentary_breakpoint (gdbarch, sal, null_frame_id, type, nostop);
+}
+
+/* Return 1 if the momentary breakpoint BP was created with the NOSTOP
+   flag.  */
+
+int
+momentary_breakpoint_is_nostop (struct breakpoint *bp)
+{
+  return bp->ops == &momentary_nostop_breakpoint_ops;
 }
 
 
@@ -9549,13 +9569,14 @@ until_break_command (char *arg, int from_tty, int anywhere)
     /* If the user told us to continue until a specified location,
        we don't specify a frame at which we need to stop.  */
     breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
-					   null_frame_id, bp_until);
+					   null_frame_id, bp_until,
+					   /* nostop */ 0);
   else
     /* Otherwise, specify the selected frame, because we want to stop
        only at the very same frame.  */
     breakpoint = set_momentary_breakpoint (get_frame_arch (frame), sal,
 					   get_stack_frame_id (frame),
-					   bp_until);
+					   bp_until, /* nostop */ 0);
 
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
@@ -9572,10 +9593,11 @@ until_break_command (char *arg, int from_tty, int anywhere)
       breakpoint2 = set_momentary_breakpoint (frame_unwind_caller_arch (frame),
 					      sal,
 					      frame_unwind_caller_id (frame),
-					      bp_until);
+					      bp_until, /* nostop */ 0);
       make_cleanup_delete_breakpoint (breakpoint2);
 
-      set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+      set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame),
+                              /* nostop */ 0);
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
 
@@ -11176,11 +11198,19 @@ momentary_bkpt_re_set (struct breakpoint *b)
 }
 
 static void
-momentary_bkpt_check_status (bpstat bs)
+momentary_stop_bkpt_check_status (bpstat bs)
 {
   /* Nothing.  The point of these breakpoints is causing a stop.  */
 }
 
+static void
+momentary_nostop_bkpt_check_status (bpstat bs)
+{
+  /* Don't stop, just trigger internal mechanisms.  */
+  bs->stop = 0;
+  bs->print = 0;
+}
+
 static enum print_stop_action
 momentary_bkpt_print_it (bpstat bs)
 {
@@ -13480,10 +13510,14 @@ initialize_breakpoint_ops (void)
   ops = &momentary_breakpoint_ops;
   *ops = bkpt_base_breakpoint_ops;
   ops->re_set = momentary_bkpt_re_set;
-  ops->check_status = momentary_bkpt_check_status;
+  ops->check_status = momentary_stop_bkpt_check_status;
   ops->print_it = momentary_bkpt_print_it;
   ops->print_mention = momentary_bkpt_print_mention;
 
+  ops = &momentary_nostop_breakpoint_ops;
+  *ops = momentary_breakpoint_ops;
+  ops->check_status = momentary_nostop_bkpt_check_status;
+
   /* GNU v3 exception catchpoints.  */
   ops = &gnu_v3_exception_catchpoint_ops;
   *ops = bkpt_breakpoint_ops;
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index c1d3be9..1b6cabd 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -836,6 +836,11 @@ struct bpstat_what
        BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.  True if we are handling a
        longjmp, false if we are handling an exception.  */
     int is_longjmp;
+
+    /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME and
+       BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.  True if we are handling a
+       nostop momentary breakpoint.  */
+    int is_nostop;
   };
 
 /* The possible return values for print_bpstat, print_it_normal,
@@ -1029,13 +1034,15 @@ extern void breakpoint_re_set (void);
 extern void breakpoint_re_set_thread (struct breakpoint *);
 
 extern struct breakpoint *set_momentary_breakpoint
-  (struct gdbarch *, struct symtab_and_line, struct frame_id, enum bptype);
+  (struct gdbarch *, struct symtab_and_line, struct frame_id, enum bptype,
+   int nostop);
 
 extern struct breakpoint *set_momentary_breakpoint_at_pc
-  (struct gdbarch *, CORE_ADDR pc, enum bptype type);
+  (struct gdbarch *, CORE_ADDR pc, enum bptype type, int nostop);
 
 extern struct breakpoint *clone_momentary_breakpoint (struct breakpoint *bpkt);
 
+extern int momentary_breakpoint_is_nostop (struct breakpoint *bp);
 extern void set_ignore_count (int, int, int);
 
 extern void breakpoint_init_inferior (enum inf_context);
@@ -1163,7 +1170,7 @@ extern int detach_breakpoints (int);
 extern void breakpoint_program_space_exit (struct program_space *pspace);
 
 extern void set_longjmp_breakpoint (struct thread_info *tp,
-				    struct frame_id frame);
+				    struct frame_id frame, int nostop);
 extern void delete_longjmp_breakpoint (int thread);
 
 extern void enable_overlay_breakpoints (void);
diff --git a/gdb/elfread.c b/gdb/elfread.c
index ddae099..6315323 100644
--- a/gdb/elfread.c
+++ b/gdb/elfread.c
@@ -993,7 +993,8 @@ elf_gnu_ifunc_resolver_stop (struct breakpoint *b)
       sal.explicit_pc = 1;
       b_return = set_momentary_breakpoint (get_frame_arch (prev_frame), sal,
 					   prev_frame_id,
-					   bp_gnu_ifunc_resolver_return);
+					   bp_gnu_ifunc_resolver_return,
+					   /* nostop */ 0);
 
       /* Add new b_return to the ring list b->related_breakpoint.  */
       gdb_assert (b_return->related_breakpoint == b_return);
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 9af56ba..cca13b2 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -782,7 +782,8 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
     /* Sanity.  The exact same SP value is returned by
        PUSH_DUMMY_CALL, saved as the dummy-frame TOS, and used by
        dummy_id to form the frame ID's stack address.  */
-    bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy);
+    bpt = set_momentary_breakpoint (gdbarch, sal, dummy_id, bp_call_dummy,
+                                    /* nostop */ 0);
     bpt->disposition = disp_del;
   }
 
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 75dc55b..5c8c9f2 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -889,7 +889,8 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
       if (in_thread_list (inferior_ptid))
  	thread = pid_to_thread_id (inferior_ptid);
 
-      set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));
+      set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()),
+                              /* nostop */ 0);
 
       make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
     }
@@ -1327,7 +1328,7 @@ until_next_command (int from_tty)
 
   tp->step_multi = 0;		/* Only one call to proceed */
 
-  set_longjmp_breakpoint (tp, get_frame_id (frame));
+  set_longjmp_breakpoint (tp, get_frame_id (frame), /* nostop */ 0);
   old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
@@ -1649,11 +1650,11 @@ finish_forward (struct symbol *function, struct frame_info *frame)
 
   breakpoint = set_momentary_breakpoint (gdbarch, sal,
 					 get_stack_frame_id (frame),
-                                         bp_finish);
+                                         bp_finish, /* nostop */ 0);
 
   old_chain = make_cleanup_delete_breakpoint (breakpoint);
 
-  set_longjmp_breakpoint (tp, get_frame_id (frame));
+  set_longjmp_breakpoint (tp, get_frame_id (frame), /* from_py*/ 0);
   make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
 
   /* We want stop_registers, please...  */
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 24d2720..dbe062a 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -55,6 +55,7 @@
 #include "continuations.h"
 #include "interps.h"
 #include "skip.h"
+#include "python/python.h"
 
 /* Prototypes for local functions */
 
@@ -104,7 +105,8 @@ static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
 
 static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
 
-static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR,
+                                              int);
 
 /* When set, stop the 'step' command if we enter a function which has
    no line number information.  The normal behavior is that we step
@@ -2379,7 +2381,8 @@ static void handle_step_into_function (struct gdbarch *gdbarch,
 static void handle_step_into_function_backward (struct gdbarch *gdbarch,
 						struct execution_control_state *ecs);
 static void check_exception_resume (struct execution_control_state *,
-				    struct frame_info *, struct symbol *);
+				    struct frame_info *, struct symbol *,
+				    int nostop);
 
 static void stop_stepping (struct execution_control_state *ecs);
 static void prepare_to_wait (struct execution_control_state *ecs);
@@ -4388,6 +4391,7 @@ process_event_stop_test:
   {
     CORE_ADDR jmp_buf_pc;
     struct bpstat_what what;
+    int nostop;
 
     what = bpstat_what (ecs->event_thread->control.stop_bpstat);
 
@@ -4434,14 +4438,15 @@ process_event_stop_test:
 	    delete_step_resume_breakpoint (ecs->event_thread);
 
 	    /* Insert a breakpoint at resume address.  */
-	    insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+	    insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc,
+	                                      what.is_nostop);
 	  }
 	else
 	  {
 	    struct symbol *func = get_frame_function (frame);
 
 	    if (func)
-	      check_exception_resume (ecs, frame, func);
+	      check_exception_resume (ecs, frame, func, what.is_nostop);
 	  }
 	keep_going (ecs);
 	return;
@@ -4455,6 +4460,8 @@ process_event_stop_test:
 	  {
 	    gdb_assert (ecs->event_thread->control.step_resume_breakpoint
 			!= NULL);
+	    nostop = momentary_breakpoint_is_nostop (
+	        ecs->event_thread->control.step_resume_breakpoint);
 	    delete_step_resume_breakpoint (ecs->event_thread);
 	  }
 	else
@@ -4476,6 +4483,8 @@ process_event_stop_test:
 
 	    gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
 			!= NULL);
+	    nostop = momentary_breakpoint_is_nostop (
+	              ecs->event_thread->control.exception_resume_breakpoint);
 	    delete_exception_resume_breakpoint (ecs->event_thread);
 
 	    if (init_frame)
@@ -4500,6 +4509,12 @@ process_event_stop_test:
 	    delete_step_resume_breakpoint (ecs->event_thread);
 	  }
 
+        /* Notify Python that an exception/longjmp occured.  */
+        gdbpy_bpfinish_handle_exception ();
+
+        if (nostop)
+          break;
+
 	ecs->event_thread->control.stop_step = 1;
 	print_end_stepping_range_reason ();
 	stop_stepping (ecs);
@@ -5385,7 +5400,8 @@ insert_step_resume_breakpoint_at_sal_1 (struct gdbarch *gdbarch,
 			paddress (gdbarch, sr_sal.pc));
 
   inferior_thread ()->control.step_resume_breakpoint
-    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type);
+    = set_momentary_breakpoint (gdbarch, sr_sal, sr_id, sr_type,
+                                /* nostop */ 0);
 }
 
 void
@@ -5466,7 +5482,8 @@ insert_step_resume_breakpoint_at_caller (struct frame_info *next_frame)
    "step-resume" breakpoints.  */
 
 static void
-insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
+insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc,
+                                  int nostop)
 {
   /* There should never be more than one step-resume or longjmp-resume
      breakpoint per thread, so we should never be setting a new
@@ -5479,20 +5496,23 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
 			paddress (gdbarch, pc));
 
   inferior_thread ()->control.step_resume_breakpoint =
-    set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
+    set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume,
+                                    nostop);
 }
 
 /* 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.  */
+   target PC of the exception.  The breakpoint will not stop the execution
+   if the flag NOSTOP is set.  */
 
 static void
 insert_exception_resume_breakpoint (struct thread_info *tp,
 				    struct block *b,
 				    struct frame_info *frame,
-				    struct symbol *sym)
+				    struct symbol *sym,
+				    int nostop)
 {
   volatile struct gdb_exception e;
 
@@ -5517,7 +5537,8 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
 				(unsigned long) handler);
 
 	  bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
-					       handler, bp_exception_resume);
+					       handler, bp_exception_resume,
+					       nostop);
 	  bp->thread = tp->num;
 	  inferior_thread ()->control.exception_resume_breakpoint = bp;
 	}
@@ -5530,7 +5551,8 @@ insert_exception_resume_breakpoint (struct thread_info *tp,
 
 static void
 check_exception_resume (struct execution_control_state *ecs,
-			struct frame_info *frame, struct symbol *func)
+			struct frame_info *frame, struct symbol *func,
+			int nostop)
 {
   volatile struct gdb_exception e;
 
@@ -5566,7 +5588,7 @@ check_exception_resume (struct execution_control_state *ecs,
 	  else
 	    {
 	      insert_exception_resume_breakpoint (ecs->event_thread,
-						  b, frame, sym);
+						  b, frame, sym, nostop);
 	      break;
 	    }
 	}
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
index bfbf9c3..621a155 100644
--- a/gdb/python/py-finishbreakpoint.c
+++ b/gdb/python/py-finishbreakpoint.c
@@ -298,6 +298,10 @@ bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   /* Bind the breakpoint with the current program space.  */
   self_bpfinish->py_bp.bp->pspace = current_program_space;
 
+  /* Set a watchdog on longjmp/exception.  It will trigger
+    `gdbpy_bpfinish_handle_exception' if necessary.  */
+  set_longjmp_breakpoint (inferior_thread (), frame_id, /* nostop */ 1);
+
   return 0;
   
  invalid_frame:
@@ -327,45 +331,74 @@ bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
   delete_breakpoint (bpfinish_obj->py_bp.bp);
 }
 
-/* Callback for `bpfinishpy_detect_out_scope'.  Triggers Python's
-   `B->out_of_scope' function if B is a FinishBreakpoint out of its scope.  */
+/* Triggers Python's `B->out_of_scope' function if B is a FinishBreakpoint
+   out of its scope.  If INC_CURRENT is set, being on the initial frame will
+   trigger the callback as well.  */
 
 static int
-bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
+bpfinishpy_detect_out_scope (struct breakpoint *b, int inc_current)
 {
   volatile struct gdb_exception except;
-  struct breakpoint *bp_stopped = (struct breakpoint *) args;
-  PyObject *py_bp = (PyObject *) b->py_bp_object;
+  struct finish_breakpoint_object *finish_bp =
+      (struct finish_breakpoint_object *) b->py_bp_object;
   struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
   
-  /* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
-     not anymore in the current callstack.  */
-  if (py_bp != NULL && b->py_bp_object->is_finish_bp)
+  /* Ensure that B is a FinishBreakpoint and we're stopped in its inferior.  */
+  if (finish_bp != NULL && finish_bp->py_bp.is_finish_bp
+      && b->pspace == current_inferior ()->pspace)
     {
-      struct finish_breakpoint_object *finish_bp =
-          (struct finish_breakpoint_object *) py_bp;
-
-      /* Check scope if not currently stopped at the FinishBreakpoint.  */
-      if (b != bp_stopped)
+      TRY_CATCH (except, RETURN_MASK_ALL)
         {
-          TRY_CATCH (except, RETURN_MASK_ALL)
-            {
-              if (b->pspace == current_inferior ()->pspace
-                  && (!target_has_registers
-                      || frame_find_by_id (b->frame_id) == NULL))
-                bpfinishpy_out_of_scope (finish_bp);
-            }
-          if (except.reason < 0)
-            {
-              gdbpy_convert_exception (except);
-              gdbpy_print_stack ();
-            }
+          /* Trigger the callback if the initial frame no longer exists or,
+           if INC_CURRENT is set, if we're back on the initial frame, or if
+           the inferior no longer exists.  */
+          int is_current = 0;
+
+          if (inc_current && target_has_registers)
+            is_current = frame_id_eq (get_frame_id (get_current_frame ()),
+                                      b->frame_id);
+
+          if (!target_has_registers
+              || frame_find_by_id (b->frame_id) == NULL
+              || is_current)
+            bpfinishpy_out_of_scope (finish_bp);
+        }
+      if (except.reason < 0)
+        {
+          gdbpy_convert_exception (except);
+          gdbpy_print_stack ();
         }
     }
 
   return 0;
 }
 
+/* Callback for bpfinishpy_handle_stop.  Check scope of B if not
+   currently stopped on it.  */
+
+static int
+bpfinishpy_detect_out_scope_bp_cb (struct breakpoint *b, void *args)
+{
+  struct breakpoint *bp_stopped = (struct breakpoint *) args;
+
+  if (bp_stopped != b)
+    bpfinishpy_detect_out_scope (b, 0);
+
+  return 0;
+}
+
+/* Callback for bpfinishpy_handle_exception.  Force out_of_scope if stopped
+   in the initial frame.  */
+
+static int
+bpfinishpy_detect_out_scope_exception_cb (struct breakpoint *b, void *args)
+{
+  bpfinishpy_detect_out_scope (b, 1);
+
+  return 0;
+}
+
+
 /* Attached to `stop' notifications, check if the execution has run
    out of the scope of any FinishBreakpoint before it has been hit.  */
 
@@ -375,22 +408,22 @@ bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
   struct cleanup *cleanup = ensure_python_env (get_current_arch (),
                                                current_language);
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_bp_cb,
                             bs == NULL ? NULL : bs->breakpoint_at);
 
   do_cleanups (cleanup);
 }
 
-/* Attached to `exit' notifications, triggers all the necessary out of
+/* Called when an exception is caught, triggers the necessary out of
    scope notifications.  */
 
-static void
-bpfinishpy_handle_exit (struct inferior *inf)
+void
+gdbpy_bpfinish_handle_exception  (void)
 {
-  struct cleanup *cleanup = ensure_python_env (target_gdbarch,
+  struct cleanup *cleanup = ensure_python_env (get_current_arch (),
                                                current_language);
 
-  iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
+  iterate_over_breakpoints (bpfinishpy_detect_out_scope_exception_cb, NULL);
 
   do_cleanups (cleanup);
 }
@@ -408,7 +441,6 @@ gdbpy_initialize_finishbreakpoints (void)
                       (PyObject *) &finish_breakpoint_object_type);
     
   observer_attach_normal_stop (bpfinishpy_handle_stop);
-  observer_attach_inferior_exit (bpfinishpy_handle_exit);
 }
 
 static PyGetSetDef finish_breakpoint_object_getset[] = {
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 5212d4e..a786a55 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1101,6 +1101,12 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj)
 		    "scripting is not supported."));
 }
 
+void
+gdbpy_bpfinish_handle_exception (void)
+{
+  /* Nothing.  */
+}
+
 #endif /* HAVE_PYTHON */
 
 
diff --git a/gdb/python/python.h b/gdb/python/python.h
index 9e461f7..8afe28f 100644
--- a/gdb/python/python.h
+++ b/gdb/python/python.h
@@ -47,4 +47,6 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj);
 
 int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj);
 
+void gdbpy_bpfinish_handle_exception (void);
+
 #endif /* GDB_PYTHON_H */
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
index 9e65d6c..ffa4f62 100644
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
@@ -50,18 +50,30 @@ if ![runto_main] then {
 # Check FinishBreakpoints against C++ exceptions
 #
 
-gdb_breakpoint [gdb_get_line_number "Break after exception 2"]
-
 gdb_test "source $pyfile" ".*Python script imported.*" \
          "import python scripts"
          
 gdb_breakpoint "throw_exception_1"
+gdb_test "python watch = gdb.Breakpoint (\"[gdb_get_line_number "Break after exception 1"]\")" \
+         "Breakpoint.*" "set watchdog breakpoint after exception handler"
 gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
 
-gdb_test "python print len(gdb.breakpoints())" "3" "check BP count"
+gdb_test "python print len(gdb.breakpoints ())" "3" "check BP count"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
-gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+
+set test "don't catch FinishBreakpoint with exception"
+gdb_test_multiple "continue" $test {
+    -re "stopped at ExceptionFinishBreakpoint.*$gdb_prompt $" {
+    	xfail $test
+    }
+    -re "exception did not finish.*Breakpoint .* Break after exception 1.*$gdb_prompt $" {
+        pass $test
+        
+        gdb_test "python print len(gdb.breakpoints())" "3" "check finish BP removal"
+    }
+}
+
+gdb_py_test_silent_cmd "python watch.delete ()" "delete watchdog breakpoint" 0
 
 gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
 gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" "init ExceptionFinishBreakpoint" "set FinishBP after the exception"
-- 
1.7.6.5


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]