Re: [RFC] Python Finish Breakpoints

Kevin Pouget
2012-01-09T09:21:12Z

[-- Attachment #1: Type: text/plain, Size: 5202 bytes --]

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.

[-- Attachment #2: 0001-better-exception-support.patch --]
[-- Type: text/x-patch, Size: 27107 bytes --]

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;
}
\f

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

\f
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