From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 10083 invoked by alias); 29 May 2009 22:32:48 -0000 Received: (qmail 10042 invoked by uid 22791); 29 May 2009 22:32:27 -0000 X-SWARE-Spam-Status: No, hits=-0.3 required=5.0 tests=AWL,BAYES_20,J_CHICKENPOX_32,KAM_STOCKGEN,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: sourceware.org Received: from mx2.redhat.com (HELO mx2.redhat.com) (66.187.237.31) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 29 May 2009 22:32:13 +0000 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n4TMWCV8021603 for ; Fri, 29 May 2009 18:32:12 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n4TMWAER029839; Fri, 29 May 2009 18:32:10 -0400 Received: from opsy.redhat.com (vpn-13-4.rdu.redhat.com [10.11.13.4]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n4TMW9F2013391; Fri, 29 May 2009 18:32:09 -0400 Received: by opsy.redhat.com (Postfix, from userid 500) id 071483786D0; Fri, 29 May 2009 16:32:08 -0600 (MDT) To: gdb-patches@sourceware.org Subject: RFC: next/finish/etc -vs- exceptions From: Tom Tromey Reply-To: Tom Tromey Date: Fri, 29 May 2009 22:32:00 -0000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-05/txt/msg00635.txt.bz2 With the current gdb, a "next" over a call that throws an exception can cause gdb to act as though the user typed "continue". This can be very frustrating when debugging C++ (or Java) code. This also happens with other commands, particularly "finish", "until", and "advance". This patch fixes this problem. It relies on a new debugging hook in GCC's unwinder, so it will only work for GCC 4.5 and later. It works by putting a breakpoint on the unwinder hook. If an exception would be thrown above the "next"ing frame, gdb will put an exception-resume breakpoint at the exception's target PC. GDB ignores other exceptions (those which would not cause a loss of control). This seems to work pretty well in my testing. However, this is my first foray into this part of GDB and I would appreciate comments on this approach. Tom 2009-05-29 Tom Tromey * infrun.c (handle_inferior_event): Handle exception breakpoints. (insert_exception_resume_breakpoint): New function. (check_exception_resume): Likewise. * inferior.h: (delete_longjmp_breakpoint_cleanup): Declare. * infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static. (step_1): Call set_exception_breakpoint. (until_next_continuation): New function. (until_next_command): Call set_exception_breakpoint. Make a continuation. (finish_command_continuation): Call delete_longjmp_breakpoint. (finish_forward): Call set_exception_breakpoint. * gdbthread.h (struct thread_info) : New field. * breakpoint.h (enum bptype) : New constants. (struct bpstat_what) : New field. * breakpoint.c (update_breakpoints_after_exec): Handle bp_exception and bp_exception_resume. (print_it_typical): Likewise. (print_one_breakpoint_location): Likewise. (allocate_bp_location): Likewise. (mention): Likewise. (breakpoint_re_set_one): Likewise. (bpstat_what): Likewise. Set 'is_longjmp' field. (delete_longjmp_breakpoint): Handle bp_exception. (set_exception_breakpoint): New function. (struct until_break_command_continuation_args) : New field. (until_break_command_continuation): Call delete_longjmp_breakpoint. (until_break_command): Call set_exception_breakpoint. 2009-05-29 Tom Tromey * gdb.cp/gdb9593.cc: New file. * gdb.cp/gdb9593.exp: New file. * gdb.cp/Makefile.in (EXECUTABLES): Add gdb9593. diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 912d7ea..2760f3e 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2,7 +2,7 @@ Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008 Free Software Foundation, Inc. + 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -1480,7 +1480,8 @@ update_breakpoints_after_exec (void) /* Longjmp and longjmp-resume breakpoints are also meaningless after an exec. */ - if (b->type == bp_longjmp || b->type == bp_longjmp_resume) + if (b->type == bp_longjmp || b->type == bp_longjmp_resume + || b->type == bp_exception || b->type == bp_exception_resume) { delete_breakpoint (b); continue; @@ -2427,6 +2428,8 @@ print_it_typical (bpstat bs) case bp_none: case bp_longjmp: case bp_longjmp_resume: + case bp_exception: + case bp_exception_resume: case bp_step_resume: case bp_watchpoint_scope: case bp_call_dummy: @@ -3266,6 +3269,7 @@ bpstat_what (bpstat bs) struct bpstat_what retval; retval.call_dummy = 0; + retval.is_longjmp = 0; for (; bs != NULL; bs = bs->next) { enum class bs_class = no_effect; @@ -3312,10 +3316,15 @@ bpstat_what (bpstat bs) bs_class = no_effect; break; case bp_longjmp: + case bp_exception: bs_class = long_jump; + retval.is_longjmp = bs->breakpoint_at->owner->type == bp_longjmp; break; case bp_longjmp_resume: + case bp_exception_resume: bs_class = long_resume; + retval.is_longjmp + = bs->breakpoint_at->owner->type == bp_longjmp_resume; break; case bp_step_resume: if (bs->stop) @@ -3518,6 +3527,8 @@ print_one_breakpoint_location (struct breakpoint *b, {bp_access_watchpoint, "acc watchpoint"}, {bp_longjmp, "longjmp"}, {bp_longjmp_resume, "longjmp resume"}, + {bp_exception, "exception"}, + {bp_exception_resume, "exception resume"}, {bp_step_resume, "step resume"}, {bp_watchpoint_scope, "watchpoint scope"}, {bp_call_dummy, "call dummy"}, @@ -3669,6 +3680,8 @@ print_one_breakpoint_location (struct breakpoint *b, case bp_finish: case bp_longjmp: case bp_longjmp_resume: + case bp_exception: + case bp_exception_resume: case bp_step_resume: case bp_watchpoint_scope: case bp_call_dummy: @@ -4244,6 +4257,8 @@ allocate_bp_location (struct breakpoint *bpt, enum bptype bp_type) case bp_finish: case bp_longjmp: case bp_longjmp_resume: + case bp_exception: + case bp_exception_resume: case bp_step_resume: case bp_watchpoint_scope: case bp_call_dummy: @@ -4462,13 +4477,43 @@ delete_longjmp_breakpoint (int thread) struct breakpoint *b, *temp; ALL_BREAKPOINTS_SAFE (b, temp) - if (b->type == bp_longjmp) + if (b->type == bp_longjmp || b->type == bp_exception) { if (b->thread == thread) delete_breakpoint (b); } } +/* Install a breakpoint on the unwinder's debug hook, and arrange for + the unwinder to call the hook when unwinding to (or past) the + current frame. */ + +CORE_ADDR +set_exception_breakpoint (struct frame_info *frame) +{ + struct minimal_symbol *debug_hook; + CORE_ADDR stack_ptr = 0; + + debug_hook = lookup_minimal_symbol_text ("_Unwind_DebugHook", NULL); + if (debug_hook != NULL) + { + struct value *lhs, *rhs; + CORE_ADDR pc; + + /* We use the current stack pointer and not the CFA here, + because the unwinder seems to compute the callee's CFA, and + so the breakpoint does not trigger. */ + stack_ptr = get_frame_sp (frame); + + pc = find_function_start_pc (get_frame_arch (frame), + SYMBOL_VALUE_ADDRESS (debug_hook), + SYMBOL_OBJ_SECTION (debug_hook)); + set_momentary_breakpoint_at_pc (pc, bp_exception); + } + + return stack_ptr; +} + static void create_overlay_event_breakpoint_1 (char *func_name, struct objfile *objfile) { @@ -5158,6 +5203,8 @@ mention (struct breakpoint *b) case bp_finish: case bp_longjmp: case bp_longjmp_resume: + case bp_exception: + case bp_exception_resume: case bp_step_resume: case bp_call_dummy: case bp_watchpoint_scope: @@ -6356,6 +6403,7 @@ struct until_break_command_continuation_args { struct breakpoint *breakpoint; struct breakpoint *breakpoint2; + int thread_num; }; /* This function is called by fetch_inferior_event via the @@ -6370,6 +6418,7 @@ until_break_command_continuation (void *arg) delete_breakpoint (a->breakpoint); if (a->breakpoint2) delete_breakpoint (a->breakpoint2); + delete_longjmp_breakpoint (a->thread_num); } void @@ -6382,6 +6431,8 @@ until_break_command (char *arg, int from_tty, int anywhere) struct breakpoint *breakpoint; struct breakpoint *breakpoint2 = NULL; struct cleanup *old_chain; + int thread; + struct thread_info *tp; clear_proceed_status (); @@ -6418,6 +6469,9 @@ until_break_command (char *arg, int from_tty, int anywhere) old_chain = make_cleanup_delete_breakpoint (breakpoint); + tp = inferior_thread (); + thread = tp->num; + /* Keep within the current frame, or in frames called by the current one. */ if (prev_frame) @@ -6427,6 +6481,10 @@ until_break_command (char *arg, int from_tty, int anywhere) breakpoint2 = set_momentary_breakpoint (sal, get_frame_id (prev_frame), bp_until); make_cleanup_delete_breakpoint (breakpoint2); + + /* Install exception-handling breakpoint. */ + tp->exception_frame = set_exception_breakpoint (prev_frame); + make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); } proceed (-1, TARGET_SIGNAL_DEFAULT, 0); @@ -6443,6 +6501,7 @@ until_break_command (char *arg, int from_tty, int anywhere) args->breakpoint = breakpoint; args->breakpoint2 = breakpoint2; + args->thread_num = thread; discard_cleanups (old_chain); add_continuation (inferior_thread (), @@ -7777,6 +7836,8 @@ breakpoint_re_set_one (void *bint) case bp_step_resume: case bp_longjmp: case bp_longjmp_resume: + case bp_exception: + case bp_exception_resume: break; } diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index a77b1b4..a5b9728 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -1,6 +1,6 @@ /* Data structures associated with breakpoints in GDB. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004, 2007, 2008 Free Software Foundation, Inc. + 2002, 2003, 2004, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -55,6 +55,9 @@ enum bptype bp_longjmp, /* secret breakpoint to find longjmp() */ bp_longjmp_resume, /* secret breakpoint to escape longjmp() */ + bp_exception, + bp_exception_resume, + /* Used by wait_for_inferior for stepping over subroutine calls, for stepping over signal handlers, and for skipping prologues. */ bp_step_resume, @@ -554,6 +557,10 @@ struct bpstat_what continuing from a call dummy without popping the frame is not a useful one). */ int call_dummy; + + /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME. True if we are + handling a longjmp, false if we are handling an exception. */ + int is_longjmp; }; /* The possible return values for print_bpstat, print_it_normal, @@ -777,6 +784,8 @@ extern int detach_breakpoints (int); extern void set_longjmp_breakpoint (void); extern void delete_longjmp_breakpoint (int thread); +extern CORE_ADDR set_exception_breakpoint (struct frame_info *); + extern void enable_overlay_breakpoints (void); extern void disable_overlay_breakpoints (void); diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h index 55c848d..b4bb97c 100644 --- a/gdb/gdbthread.h +++ b/gdb/gdbthread.h @@ -171,6 +171,9 @@ struct thread_info /* True if this thread has been explicitly requested to stop. */ int stop_requested; + /* The target CFA of an exception. */ + CORE_ADDR exception_frame; + /* Private data used by the target vector implementation. */ struct private_thread_info *private; }; diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 50e8fff..a3ed951 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -2,7 +2,7 @@ Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008 Free Software Foundation, Inc. + 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -754,7 +754,7 @@ nexti_command (char *count_string, int from_tty) step_1 (1, 1, count_string); } -static void +void delete_longjmp_breakpoint_cleanup (void *arg) { int thread = * (int *) arg; @@ -793,11 +793,13 @@ step_1 (int skip_subroutines, int single_inst, char *count_string) if (!single_inst || skip_subroutines) /* leave si command alone */ { + struct thread_info *tp = inferior_thread (); + if (in_thread_list (inferior_ptid)) thread = pid_to_thread_id (inferior_ptid); set_longjmp_breakpoint (); - + tp->exception_frame = set_exception_breakpoint (get_current_frame ()); make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); } @@ -1154,6 +1156,15 @@ signal_command (char *signum_exp, int from_tty) proceed (oursig == TARGET_SIGNAL_0 ? (CORE_ADDR) -1 : stop_pc, oursig, 0); } +/* A continuation callback for until_next_command. */ + +static void +until_next_continuation (void *arg) +{ + struct thread_info *tp = arg; + delete_longjmp_breakpoint (tp->num); +} + /* Proceed until we reach a different source line with pc greater than our current one or exit the function. We skip calls in both cases. @@ -1170,6 +1181,8 @@ until_next_command (int from_tty) struct symbol *func; struct symtab_and_line sal; struct thread_info *tp = inferior_thread (); + int thread = tp->num; + struct cleanup *old_chain; clear_proceed_status (); @@ -1205,7 +1218,18 @@ until_next_command (int from_tty) tp->step_multi = 0; /* Only one call to proceed */ + tp->exception_frame = set_exception_breakpoint (frame); + old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1); + + if (target_can_async_p () && is_running (inferior_ptid)) + { + discard_cleanups (old_chain); + add_continuation (tp, until_next_continuation, tp, NULL); + } + else + do_cleanups (old_chain); } static void @@ -1383,6 +1407,7 @@ finish_command_continuation (void *arg) observer_notify_normal_stop (bs); suppress_stop_observer = 0; delete_breakpoint (a->breakpoint); + delete_longjmp_breakpoint (inferior_thread ()->num); } static void @@ -1463,6 +1488,7 @@ finish_forward (struct symbol *function, struct frame_info *frame) struct breakpoint *breakpoint; struct cleanup *old_chain; struct finish_command_continuation_args *cargs; + int thread = tp->num; sal = find_pc_line (get_frame_pc (frame), 0); sal.pc = get_frame_pc (frame); @@ -1472,6 +1498,9 @@ finish_forward (struct symbol *function, struct frame_info *frame) old_chain = make_cleanup_delete_breakpoint (breakpoint); + tp->exception_frame = set_exception_breakpoint (frame); + make_cleanup (delete_longjmp_breakpoint_cleanup, &thread); + tp->proceed_to_finish = 1; /* We want stop_registers, please... */ make_cleanup_restore_integer (&suppress_stop_observer); suppress_stop_observer = 1; diff --git a/gdb/inferior.h b/gdb/inferior.h index cc5bf9f..3e62a2f 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -2,7 +2,7 @@ Where it is, why it stopped, and how to step it. Copyright (C) 1986, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, - 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008 + 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -269,6 +269,8 @@ extern void interrupt_target_command (char *args, int from_tty); extern void interrupt_target_1 (int all_threads); +extern void delete_longjmp_breakpoint_cleanup (void *arg); + /* Address at which inferior stopped. */ extern CORE_ADDR stop_pc; diff --git a/gdb/infrun.c b/gdb/infrun.c index 30d914d..8997cf5 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3,7 +3,7 @@ Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2008 Free Software Foundation, Inc. + 2008, 2009 Free Software Foundation, Inc. This file is part of GDB. @@ -45,6 +45,8 @@ #include "language.h" #include "solib.h" #include "main.h" +#include "dictionary.h" +#include "block.h" #include "gdb_assert.h" #include "mi/mi-common.h" @@ -1551,6 +1553,8 @@ static void insert_step_resume_breakpoint_at_caller (struct frame_info *); static void insert_step_resume_breakpoint_at_sal (struct symtab_and_line sr_sal, struct frame_id sr_id); static void insert_longjmp_resume_breakpoint (CORE_ADDR); +static void check_exception_resume (struct execution_control_state *, + struct frame_info *, struct symbol *); static void stop_stepping (struct execution_control_state *ecs); static void prepare_to_wait (struct execution_control_state *ecs); @@ -3019,24 +3023,34 @@ process_event_stop_test: ecs->event_thread->stepping_over_breakpoint = 1; - if (!gdbarch_get_longjmp_target_p (current_gdbarch) - || !gdbarch_get_longjmp_target (current_gdbarch, - get_current_frame (), &jmp_buf_pc)) + if (what.is_longjmp) { - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, "\ + if (!gdbarch_get_longjmp_target_p (current_gdbarch) + || !gdbarch_get_longjmp_target (current_gdbarch, + get_current_frame (), &jmp_buf_pc)) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "\ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n"); - keep_going (ecs); - return; - } + keep_going (ecs); + return; + } - /* We're going to replace the current step-resume breakpoint - with a longjmp-resume breakpoint. */ - delete_step_resume_breakpoint (ecs->event_thread); + /* We're going to replace the current step-resume breakpoint + with a longjmp-resume breakpoint. */ + delete_step_resume_breakpoint (ecs->event_thread); - /* Insert a breakpoint at resume address. */ - insert_longjmp_resume_breakpoint (jmp_buf_pc); + /* Insert a breakpoint at resume address. */ + insert_longjmp_resume_breakpoint (jmp_buf_pc); + } + else + { + struct frame_info *frame = get_current_frame (); + struct symbol *func = get_frame_function (frame); + if (func) + check_exception_resume (ecs, frame, func); + } keep_going (ecs); return; @@ -3048,6 +3062,17 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n"); gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL); delete_step_resume_breakpoint (ecs->event_thread); + if (!what.is_longjmp && ecs->event_thread->step_range_start + && (get_frame_sp (get_current_frame ()) + == ecs->event_thread->exception_frame)) + { + /* The inferior threw an exception which landed in the + frame in which we started stepping. In this case we + defer to the stepping logic to decide whether to stop. + In all other exception cases, we always stop. */ + break; + } + ecs->event_thread->stop_step = 1; print_stop_reason (END_STEPPING_RANGE, 0); stop_stepping (ecs); @@ -3851,6 +3876,112 @@ insert_longjmp_resume_breakpoint (CORE_ADDR pc) set_momentary_breakpoint_at_pc (pc, bp_longjmp_resume); } +/* Insert an exception resume breakpoint. TP is the thread throwing + the exception. The block B is the block of the unwinder debug hook + function. FRAME is the frame corresponding to the call to this + function. SYM is the symbol of the function argument holding the + target PC of the exception. */ + +static void +insert_exception_resume_breakpoint (struct thread_info *tp, + struct block *b, + struct frame_info *frame, + struct symbol *sym) +{ + struct gdb_exception e; + + /* We want to ignore errors here. */ + TRY_CATCH (e, RETURN_MASK_ALL) + { + struct symbol *vsym; + struct value *value; + CORE_ADDR handler; + struct breakpoint *bp; + + vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL); + value = read_var_value (vsym, frame); + handler = value_as_address (value); + + /* We're going to replace the current step-resume breakpoint + with a longjmp-resume breakpoint. */ + delete_step_resume_breakpoint (tp); + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: exception resume at %lx\n", + (unsigned long) handler); + + bp = set_momentary_breakpoint_at_pc (handler, bp_exception_resume); + inferior_thread ()->step_resume_breakpoint = bp; + } +} + +/* This is called when an exception has been intercepted. Check to + see whether the exception's destination is of interest, and if so, + set an exception resume breakpoint there. */ + +static void +check_exception_resume (struct execution_control_state *ecs, + struct frame_info *frame, struct symbol *func) +{ + struct gdb_exception e; + + TRY_CATCH (e, RETURN_MASK_ALL) + { + struct block *b; + struct dict_iterator iter; + struct symbol *sym; + int argno = 0; + + b = SYMBOL_BLOCK_VALUE (func); + ALL_BLOCK_SYMBOLS (b, iter, sym) + { + if (!SYMBOL_IS_ARGUMENT (sym)) + continue; + + if (argno == 0) + { + struct symbol *vsym; + struct value *value; + CORE_ADDR cfa, nexting_cfa; + struct gdbarch *arch; + + vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), + b, VAR_DOMAIN, NULL); + value = read_var_value (vsym, frame); + cfa = value_as_address (value); + + arch = get_frame_arch (frame); + nexting_cfa = ecs->event_thread->exception_frame; + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: comparing exception target %lx with next-frame %lx: %d\n", + (unsigned long) cfa, + (unsigned long) nexting_cfa, + !nexting_cfa || gdbarch_inner_than (arch, cfa, nexting_cfa)); + + if (!nexting_cfa + || gdbarch_inner_than (arch, cfa, nexting_cfa)) + { + /* Not an interesting exception. */ + break; + } + ++argno; + } + else + { + /* If we got here, then we want to set the exception + resume breakpoint at the address held in the second + argument to the function. */ + + insert_exception_resume_breakpoint (ecs->event_thread, + b, frame, sym); + break; + } + } + } +} + static void stop_stepping (struct execution_control_state *ecs) { @@ -3917,6 +4048,8 @@ keep_going (struct execution_control_state *ecs) } if (e.reason < 0) { + if (debug_infrun) + exception_fprintf (gdb_stdlog, e, "infrun: exception while inserting breakpoints: "); stop_stepping (ecs); return; } diff --git a/gdb/testsuite/gdb.cp/Makefile.in b/gdb/testsuite/gdb.cp/Makefile.in index f4a989c..1a2b908 100644 --- a/gdb/testsuite/gdb.cp/Makefile.in +++ b/gdb/testsuite/gdb.cp/Makefile.in @@ -4,7 +4,7 @@ srcdir = @srcdir@ EXECUTABLES = ambiguous annota2 anon-union cplusfuncs cttiadd \ derivation inherit local member-ptr method misc \ overload ovldbreak ref-typ ref-typ2 templates userdef virtfunc namespace \ - ref-types ref-params method2 + ref-types ref-params method2 gdb9593 all info install-info dvi install uninstall installcheck check: @echo "Nothing to be done for $@..." diff --git a/gdb/testsuite/gdb.cp/gdb9593.cc b/gdb/testsuite/gdb.cp/gdb9593.cc new file mode 100644 index 0000000..3092f57 --- /dev/null +++ b/gdb/testsuite/gdb.cp/gdb9593.cc @@ -0,0 +1,180 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2008, 2009 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ +#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 (); + } + + void until () + { + function1 (); + function1 (); // until here + } + +}; +NextOverThrowDerivates next_cases; + + +int main () +{ + try + { + next_cases.function1 (); + } + catch (...) + { + // Discard + } + + try + { + next_cases.function2 (); + } + catch (...) + { + // Discard + } + + try + { + // This is duplicated so we can next over one but step into + // another. + next_cases.function2 (); + } + catch (...) + { + // Discard + } + + next_cases.function3 (); + + try + { + next_cases.rethrow (); + } + catch (...) + { + // Discard + } + + try + { + // Another duplicate so we can test "finish". + next_cases.function2 (); + } + catch (...) + { + // Discard + } + + // Another test for "finish". + try + { + next_cases.finish (); + } + catch (...) + { + } + + // Test of "until". + try + { + next_cases.finish (); + } + catch (...) + { + } + + // Test of "until" with an argument. + try + { + next_cases.until (); + } + catch (...) + { + } + + // Test of "advance". + try + { + next_cases.until (); + } + catch (...) + { + } +} + diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp new file mode 100644 index 0000000..b0fcfbc --- /dev/null +++ b/gdb/testsuite/gdb.cp/gdb9593.exp @@ -0,0 +1,183 @@ +# Copyright 2008, 2009 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +if $tracelevel then { + strace $tracelevel +} + +if { [skip_cplus_tests] } { continue } + +set prms_id 9593 +set bug_id 0 + +set testfile "gdb9593" +set srcfile ${testfile}.cc +set binfile $objdir/$subdir/$testfile + +# Create and source the file that provides information about the compiler +# used to compile the test case. +if [get_compiler_info ${binfile} "c++"] { + untested gdb9593.exp + return -1 +} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + untested gdb9593.exp + return -1 +} + +# Some targets can't do function calls, so don't even bother with this +# test. +if [target_info exists gdb,cannot_call_functions] { + setup_xfail "*-*-*" 9593 + fail "This target can not call functions" + continue +} + +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if ![runto_main] then { + perror "couldn't run to main" + continue +} + +# See whether we have the needed unwinder hooks. +set ok 1 +gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" { + -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" { + pass "check for unwinder hook" + } + -re "No symbol .* in current context.\r\n$gdb_prompt $" { + # Pass the test so we don't get bogus fails in the results. + pass "check for unwinder hook" + set ok 0 + } +} +if {!$ok} { + untested gdb9593.exp + return -1 +} + +# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593 + +gdb_test "next" \ + ".*catch (...).*" \ + "next over a throw 1" + +gdb_test "next" \ + ".*next_cases.function2.*" \ + "next past catch 1" + +gdb_test "next" \ + ".*catch (...).*" \ + "next over a throw 2" + +gdb_test "next" \ + ".*next_cases.function2.*" \ + "next past catch 2" + +gdb_test "step" \ + ".*function1().*" \ + "step into function2 1" + +gdb_test "next" \ + ".*catch (...).*" \ + "next over a throw 3" + +gdb_test "next" \ + ".*next_cases.function3.*" \ + "next past catch 3" + +gdb_test "next" \ + ".*next_cases.rethrow.*" \ + "next over a throw 4" + +gdb_test "next" \ + ".*catch (...).*" \ + "next over a rethrow" + +gdb_test "next" \ + ".*next_cases.function2.*" \ + "next after a rethrow" + +gdb_test "step" \ + ".*function1().*" \ + "step into function2 2" + +gdb_test "finish" \ + ".*catch (...).*" \ + "finish 1" + +gdb_test "next" \ + ".*next_cases.finish ().*" \ + "next past catch 4" + +gdb_test "step" \ + ".*function1 ().*" \ + "step into finish method" + +gdb_test "finish" \ + ".*catch (...).*" \ + "finish 2" + +gdb_test "next" \ + ".*next_cases.finish ().*" \ + "next past catch 5" + +gdb_test "step" \ + ".*function1 ().*" \ + "step into finish, for until" + +gdb_test "until" \ + ".*catch .int x.*" \ + "until with no argument 1" + +gdb_test "next" \ + ".*function1 ().*" \ + "next past catch 6" + +gdb_test "until" \ + ".*catch (...).*" \ + "until with no argument 2" + +set line [gdb_get_line_number "until here" $testfile.cc] + +gdb_test "next" \ + ".*next_cases.until ().*" \ + "next past catch 6" + +gdb_test "step" \ + ".*function1 ().*" \ + "step into until" + +gdb_test "until $line" \ + ".*catch (...).*" \ + "until-over-throw" + +gdb_test "next" \ + ".*next_cases.until ().*" \ + "next past catch 7" + +gdb_test "step" \ + ".*function1 ().*" \ + "step into until, for advance" + +gdb_test "advance $line" \ + ".*catch (...).*" \ + "advance-over-throw"