http://sourceware.org/ml/gdb-patches/2010-10/msg00109.html Subject: RFC: next/finish/etc -vs- exceptions 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 * 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) : New field. * breakpoint.h (enum bptype) : New constants. (struct bpstat_what) : 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) : 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 * gdb.java/jnpe.java: New file. * gdb.java/jnpe.exp: New file. * gdb.cp/gdb9593.exp: New file. * gdb.cp/gdb9593.cc: New file. Index: gdb-7.2.50.20101116/gdb/breakpoint.c =================================================================== --- gdb-7.2.50.20101116.orig/gdb/breakpoint.c 2010-11-17 02:38:59.000000000 +0100 +++ gdb-7.2.50.20101116/gdb/breakpoint.c 2010-11-17 02:41:53.000000000 +0100 @@ -2232,6 +2232,33 @@ create_std_terminate_master_breakpoint ( 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) { @@ -2273,7 +2300,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; @@ -2288,7 +2316,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; @@ -2350,6 +2379,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 @@ -3275,6 +3305,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); @@ -3362,6 +3398,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: @@ -4164,7 +4202,8 @@ bpstat_stop_status (struct address_space 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 @@ -4260,6 +4299,7 @@ bpstat_what (bpstat bs_head) retval.main_action = BPSTAT_WHAT_KEEP_CHECKING; retval.call_dummy = STOP_NONE; + retval.is_longjmp = 0; for (bs = bs_head; bs != NULL; bs = bs->next) { @@ -4315,10 +4355,14 @@ bpstat_what (bpstat bs_head) } 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) @@ -4334,6 +4378,7 @@ bpstat_what (bpstat bs_head) 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: @@ -4556,6 +4601,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"}, @@ -4565,6 +4612,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"}, @@ -4705,6 +4753,8 @@ print_one_breakpoint_location (struct br 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: @@ -4714,6 +4764,7 @@ print_one_breakpoint_location (struct br 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: @@ -5457,6 +5508,8 @@ allocate_bp_location (struct breakpoint 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: @@ -5469,6 +5522,7 @@ allocate_bp_location (struct breakpoint case bp_std_terminate_master: case bp_gnu_ifunc_resolver: case bp_gnu_ifunc_resolver_return: + case bp_exception_master: loc->loc_type = bp_loc_software_breakpoint; break; case bp_hardware_breakpoint: @@ -5708,8 +5762,7 @@ make_breakpoint_permanent (struct breakp } /* 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) @@ -5722,11 +5775,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; } } @@ -5738,7 +5792,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); @@ -6913,6 +6967,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: @@ -6924,6 +6980,7 @@ mention (struct breakpoint *b) case bp_longjmp_master: case bp_std_terminate_master: case bp_gnu_ifunc_resolver_return: + case bp_exception_master: break; } @@ -8598,6 +8655,7 @@ struct until_break_command_continuation_ { struct breakpoint *breakpoint; struct breakpoint *breakpoint2; + int thread_num; }; /* This function is called by fetch_inferior_event via the @@ -8612,6 +8670,7 @@ until_break_command_continuation (void * delete_breakpoint (a->breakpoint); if (a->breakpoint2) delete_breakpoint (a->breakpoint2); + delete_longjmp_breakpoint (a->thread_num); } void @@ -8623,6 +8682,8 @@ until_break_command (char *arg, int from struct breakpoint *breakpoint; struct breakpoint *breakpoint2 = NULL; struct cleanup *old_chain; + int thread; + struct thread_info *tp; clear_proceed_status (); @@ -8661,6 +8722,9 @@ until_break_command (char *arg, int from 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. */ @@ -8673,6 +8737,10 @@ until_break_command (char *arg, int from 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); @@ -8689,6 +8757,7 @@ until_break_command (char *arg, int from args->breakpoint = breakpoint; args->breakpoint2 = breakpoint2; + args->thread_num = thread; discard_cleanups (old_chain); add_continuation (inferior_thread (), @@ -9940,6 +10009,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; @@ -9961,6 +10031,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); } @@ -10470,6 +10541,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; @@ -10493,6 +10565,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: case bp_gnu_ifunc_resolver_return: break; @@ -10537,6 +10611,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 (); } /* Reset the thread number of this breakpoint: Index: gdb-7.2.50.20101116/gdb/breakpoint.h =================================================================== --- gdb-7.2.50.20101116.orig/gdb/breakpoint.h 2010-11-17 02:38:59.000000000 +0100 +++ gdb-7.2.50.20101116/gdb/breakpoint.h 2010-11-17 02:39:19.000000000 +0100 @@ -61,6 +61,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, @@ -130,6 +137,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, @@ -672,6 +682,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, Index: gdb-7.2.50.20101116/gdb/gdbthread.h =================================================================== --- gdb-7.2.50.20101116.orig/gdb/gdbthread.h 2010-11-02 02:37:31.000000000 +0100 +++ gdb-7.2.50.20101116/gdb/gdbthread.h 2010-11-17 02:39:19.000000000 +0100 @@ -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; Index: gdb-7.2.50.20101116/gdb/infcmd.c =================================================================== --- gdb-7.2.50.20101116.orig/gdb/infcmd.c 2010-07-01 17:36:15.000000000 +0200 +++ gdb-7.2.50.20101116/gdb/infcmd.c 2010-11-17 02:39:19.000000000 +0100 @@ -822,7 +822,7 @@ nexti_command (char *count_string, int f 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 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 fr 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 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, 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)); Index: gdb-7.2.50.20101116/gdb/inferior.h =================================================================== --- gdb-7.2.50.20101116.orig/gdb/inferior.h 2010-11-17 02:38:55.000000000 +0100 +++ gdb-7.2.50.20101116/gdb/inferior.h 2010-11-17 02:39:19.000000000 +0100 @@ -299,6 +299,8 @@ extern void interrupt_target_command (ch 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); Index: gdb-7.2.50.20101116/gdb/infrun.c =================================================================== --- gdb-7.2.50.20101116.orig/gdb/infrun.c 2010-11-17 02:38:59.000000000 +0100 +++ gdb-7.2.50.20101116/gdb/infrun.c 2010-11-17 02:39:19.000000000 +0100 @@ -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" @@ -2229,6 +2231,8 @@ static void insert_step_resume_breakpoin 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); @@ -4121,23 +4125,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; @@ -4149,6 +4163,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME ( 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); @@ -5127,6 +5188,100 @@ insert_longjmp_resume_breakpoint (struct 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) { Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.cc =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.cc 2010-11-17 02:39:19.000000000 +0100 @@ -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 . + */ +#include + +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 (...) + { + } +} + Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.2.50.20101116/gdb/testsuite/gdb.cp/gdb9593.exp 2010-11-17 02:39:19.000000000 +0100 @@ -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 . + + +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" Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.exp =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.exp 2010-11-17 02:39:19.000000000 +0100 @@ -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 . + +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.*" Index: gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.java =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ gdb-7.2.50.20101116/gdb/testsuite/gdb.java/jnpe.java 2010-11-17 02:39:19.000000000 +0100 @@ -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 . + */ + +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 + } + } +}