* RFC: next/finish/etc -vs- exceptions
@ 2009-05-29 22:32 Tom Tromey
2009-05-30 21:08 ` Doug Evans
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: Tom Tromey @ 2009-05-29 22:32 UTC (permalink / raw)
To: gdb-patches
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 <tromey@redhat.com>
* 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) <exception_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume>:
New constants.
(struct bpstat_what) <is_longjmp>: 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) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Call set_exception_breakpoint.
2009-05-29 Tom Tromey <tromey@redhat.com>
* 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 <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1()
+ {
+ throw 20;
+ }
+
+ // Throw an exception in another function.
+ void function2()
+ {
+ function1();
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 ()
+ {
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow ()
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish ()
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 ();
+ }
+ catch (int x)
+ {
+ }
+ function1 ();
+ }
+
+ 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 <http://www.gnu.org/licenses/>.
+
+
+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"
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-05-29 22:32 RFC: next/finish/etc -vs- exceptions Tom Tromey
@ 2009-05-30 21:08 ` Doug Evans
2009-05-30 23:18 ` Tom Tromey
2009-06-10 16:13 ` Joel Brobecker
2009-07-24 17:38 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Doug Evans @ 2009-05-30 21:08 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Hi. My limited knowledge didn't find anything seriously broken 1/2
:-), but I do have a few comments.
On Fri, May 29, 2009 at 3:32 PM, Tom Tromey <tromey@redhat.com> wrote:
> 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.
I wouldn't block on this, but one aspect of good u/i is setting expectations.
If something is known to not work, telling the user up front is
usually better than staying silent and leaving it to the user to
figure out.
To that end, should gdb print a warning if _Unwind_DebugHook isn't found?
Your patch isn't alone in this, and I'm not sure when and how to print
such warnings.
It just seems to me to be a good idea to have such warnings.
[To minimize the verbosity, gdb could print a general warning when it
detects such conditions, with instructions on how to get further
details. E.g. "GDB has detected conditions that will hinder some
debugging efforts. Use `mumble' for further information." or some
such. That way the user might just get one warning at startup, and
that's it. And we could expand on the conditions to detect without
affecting the basic session flow.]
The user might get used to the message, start ignoring it, etc. etc.
Maybe it could be off by default.
The high order bit though is that there would be standard things the
user can do to figure out why (e.g. in this case) "next" isn't working
the way s/he is expecting it to (for typically frequent cases that are
reasonable to handle - stripped libpthread anyone?).
Just a thought.
> @@ -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
Having to record the thread here seems orthogonal to handling next-ing
over exceptions (e.g. why isn't it also required for longjmp?).
I wouldn't necessarily put it in a separate patch though (regardless
of whether it is or isn't orthogonal).
Maybe add further comments explaining what's going on here?
> @@ -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. */
s/longjmp/exception/ ?
> + 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;
It's a bit odd to check function arguments for something like this.
But maybe I just don't understand the implementation enough.
There's an implementation aspect here that's not clear to me.
Can you add comments elaborating on the argno == 0 vs argno > 0 cases?
Can we assume that function arguments will be seen in program order here?
[or am I misinterpreting argno == 0 vs != 0 ?]
> +
> + 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)
> {
> [...]
> 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 $@..."
Blech. I haven't been paying attention to this myself.
It's really nice to be able to add new testcases *without* having to
edit any files like Makefile.in.
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-05-30 21:08 ` Doug Evans
@ 2009-05-30 23:18 ` Tom Tromey
0 siblings, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2009-05-30 23:18 UTC (permalink / raw)
To: Doug Evans; +Cc: gdb-patches
>>>>> "Doug" == Doug Evans <dje@google.com> writes:
Doug> Hi. My limited knowledge didn't find anything seriously broken 1/2
Doug> :-), but I do have a few comments.
Thanks!
Doug> To that end, should gdb print a warning if _Unwind_DebugHook isn't found?
Doug> Your patch isn't alone in this, and I'm not sure when and how to print
Doug> such warnings.
I wonder about this as well.
Adding a warning in this case is a bit unusual, in that many programs
do not use exceptions. E.g., getting this warning when debugging an
ordinary C program would seem weird. Maybe there is some way around
that.
Doug> [To minimize the verbosity, gdb could print a general warning when it
Doug> detects such conditions, with instructions on how to get further
Doug> details. E.g. "GDB has detected conditions that will hinder some
Doug> debugging efforts. Use `mumble' for further information." or some
Doug> such.
Yeah. We could add useful text in "help" somewhere. Maybe "help faq".
Doug> That way the user might just get one warning at startup, and
Doug> that's it.
... once per affected inferior, I think.
Doug> The high order bit though is that there would be standard things the
Doug> user can do to figure out why (e.g. in this case) "next" isn't working
Doug> the way s/he is expecting it to (for typically frequent cases that are
Doug> reasonable to handle - stripped libpthread anyone?).
I like this idea.
Tom> @@ -6356,6 +6403,7 @@ struct until_break_command_continuation_args
Tom> {
Tom> struct breakpoint *breakpoint;
Tom> struct breakpoint *breakpoint2;
Tom> + int thread_num;
Tom> };
Doug> Having to record the thread here seems orthogonal to handling next-ing
Doug> over exceptions (e.g. why isn't it also required for longjmp?).
IIRC, the longjmp code works for 'next' but not for 'until' (at least,
my recollection is that longjmp does not work for all these commands;
I don't remember exactly which ones).
The reason this field is here is so that we can delete the exception
breakpoint when cleaning up after the 'until'. The exception
breakpoints are all thread-specific; recording the thread number is
convenient since we already have delete_longjmp_breakpoint.
Doug> Maybe add further comments explaining what's going on here?
Will do.
Tom> + /* We're going to replace the current step-resume breakpoint
Tom> + with a longjmp-resume breakpoint. */
Doug> s/longjmp/exception/ ?
Oops, thanks.
Tom> + if (!SYMBOL_IS_ARGUMENT (sym))
Tom> + continue;
Doug> It's a bit odd to check function arguments for something like this.
Doug> But maybe I just don't understand the implementation enough.
Doug> There's an implementation aspect here that's not clear to me.
Doug> Can you add comments elaborating on the argno == 0 vs argno > 0 cases?
I will add a comment. Meanwhile, here's the explanation.
I added a function to the unwinder:
/* This function is called during unwinding. It is intended as a hook
for a debugger to intercept exceptions. CFA is the CFA of the
target frame. HANDLER is the PC to which control will be
transferred. */
static void
_Unwind_DebugHook (void *cfa __attribute__ ((__unused__)),
void *handler __attribute__ ((__unused__)))
At 'next' time, gdb sets the "exception breakpoint" on this function.
To use it, gdb then examines the arguments to the function. If the
target frame, as represented by the CFA argument, is interesting
(e.g., for 'next', somewhere in the current frame or above), then we
set a temporary breakpoint at HANDLER.
Doug> Blech. I haven't been paying attention to this myself.
Doug> It's really nice to be able to add new testcases *without* having to
Doug> edit any files like Makefile.in.
Yes, I also am not a fan of this, or for that matter of the testsuite
configure script and all the testsuite/*/Makefile.in files. I don't
have an alternate proposal right now, though.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-05-29 22:32 RFC: next/finish/etc -vs- exceptions Tom Tromey
2009-05-30 21:08 ` Doug Evans
@ 2009-06-10 16:13 ` Joel Brobecker
2009-06-10 16:50 ` Tom Tromey
2009-07-24 17:38 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Joel Brobecker @ 2009-06-10 16:13 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Hey Tom,
I can't seem to find the time to look at this in more details while
I'm at the summit, but I always find myself wondering whether it might
have been possible to use the breakpoint_ops structure rather than
introducing a couple of breakpoint kinds... I glanced quickly at the
patch, and I don't think this is the case in this particular instance,
but did you also look at this option, eventually ruling it out?
The feature itself is really interesting, and I wonder if there might
be something we can do for Ada as well. As far as I know, there is no
hook just yet, but maybe we can have it added as well. After all, I know
a few people who work on the Ada compiler ;-).
Just my few cents.
--
Joel
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 16:13 ` Joel Brobecker
@ 2009-06-10 16:50 ` Tom Tromey
2009-06-10 17:05 ` Pedro Alves
2009-06-11 14:45 ` Joel Brobecker
0 siblings, 2 replies; 40+ messages in thread
From: Tom Tromey @ 2009-06-10 16:50 UTC (permalink / raw)
To: Joel Brobecker; +Cc: gdb-patches
>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:
Joel> I can't seem to find the time to look at this in more details while
Joel> I'm at the summit, but I always find myself wondering whether it might
Joel> have been possible to use the breakpoint_ops structure rather than
Joel> introducing a couple of breakpoint kinds... I glanced quickly at the
Joel> patch, and I don't think this is the case in this particular instance,
Joel> but did you also look at this option, eventually ruling it out?
Actually I just looked at the longjmp code and did something very
similar. I will take a look at doing this.
It would be nice if we had a flag day and simply converted everything
to breakpoint_ops. That's usually the best way to ensure that new
code is written the "right" way (gcc has also had some bad experiences
with these incomplete transitions).
Also, I recently realized that the patch computes the CFA incorrectly.
Really it ought to always compute the Dwarf CFA. I didn't see a nice
way to do that; if you have any suggestions...
Joel> The feature itself is really interesting, and I wonder if there might
Joel> be something we can do for Ada as well. As far as I know, there is no
Joel> hook just yet, but maybe we can have it added as well. After all, I know
Joel> a few people who work on the Ada compiler ;-).
I was under the impression that Ada used longjmp to implement
exceptions, and so the existing longjmp support should work. (Modulo
the odd bug or two -- I'm not sure if Pedro's fix for the glibc
pointer mangling went in or not.)
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 16:50 ` Tom Tromey
@ 2009-06-10 17:05 ` Pedro Alves
2009-06-10 17:13 ` Daniel Jacobowitz
2009-06-11 14:45 ` Joel Brobecker
1 sibling, 1 reply; 40+ messages in thread
From: Pedro Alves @ 2009-06-10 17:05 UTC (permalink / raw)
To: gdb-patches, tromey; +Cc: Joel Brobecker
On Wednesday 10 June 2009 17:50:18, Tom Tromey wrote:
> I was under the impression that Ada used longjmp to implement
> exceptions, and so the existing longjmp support should work. (Modulo
> the odd bug or two -- I'm not sure if Pedro's fix for the glibc
> pointer mangling went in or not.)
It did not. Our longjmp support on glibc is still broken.
I assume that exceptions have a property that raw longjmp doesn't,
which is what makes stepping over longjmp complicated --- they never
switch to alternate stacks? That was one reason why comparing
frame/stack pointers with inner_than kind of comparisions is
verboten (to know when the longjmp/exception is all inner to the
step/next and can be ignored, for example). That, and frames
crossing architectures, like on Cell. I did spot one such comparision
by a quick look at the patch, but I don't know if it OK to do so in
your case or not. I assume other people who know more about
frames and unwinders than me can provide better input there. :-)
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 17:05 ` Pedro Alves
@ 2009-06-10 17:13 ` Daniel Jacobowitz
2009-06-10 17:47 ` Pedro Alves
2010-11-25 4:54 ` Jan Kratochvil
0 siblings, 2 replies; 40+ messages in thread
From: Daniel Jacobowitz @ 2009-06-10 17:13 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, tromey, Joel Brobecker
On Wed, Jun 10, 2009 at 06:06:31PM +0100, Pedro Alves wrote:
> I assume that exceptions have a property that raw longjmp doesn't,
> which is what makes stepping over longjmp complicated --- they never
> switch to alternate stacks? That was one reason why comparing
> frame/stack pointers with inner_than kind of comparisions is
> verboten (to know when the longjmp/exception is all inner to the
> step/next and can be ignored, for example). That, and frames
> crossing architectures, like on Cell. I did spot one such comparision
> by a quick look at the patch, but I don't know if it OK to do so in
> your case or not. I assume other people who know more about
> frames and unwinders than me can provide better input there. :-)
Not sure what you mean exactly, but exceptions can be thrown through
signal handlers on many platforms; so yes, they might switch stack.
They could go to any stack higher on the call frame.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 17:13 ` Daniel Jacobowitz
@ 2009-06-10 17:47 ` Pedro Alves
2009-06-10 18:32 ` Tom Tromey
2009-06-12 20:49 ` Tom Tromey
2010-11-25 4:54 ` Jan Kratochvil
1 sibling, 2 replies; 40+ messages in thread
From: Pedro Alves @ 2009-06-10 17:47 UTC (permalink / raw)
To: gdb-patches; +Cc: Daniel Jacobowitz, tromey, Joel Brobecker
On Wednesday 10 June 2009 18:13:28, Daniel Jacobowitz wrote:
> On Wed, Jun 10, 2009 at 06:06:31PM +0100, Pedro Alves wrote:
> > I assume that exceptions have a property that raw longjmp doesn't,
> > which is what makes stepping over longjmp complicated --- they never
> > switch to alternate stacks? That was one reason why comparing
> > frame/stack pointers with inner_than kind of comparisions is
> > verboten (to know when the longjmp/exception is all inner to the
> > step/next and can be ignored, for example). That, and frames
> > crossing architectures, like on Cell. I did spot one such comparision
> > by a quick look at the patch, but I don't know if it OK to do so in
> > your case or not. I assume other people who know more about
> > frames and unwinders than me can provide better input there. :-)
>
> Not sure what you mean exactly, but exceptions can be thrown through
> signal handlers on many platforms; so yes, they might switch stack.
> They could go to any stack higher on the call frame.
>
Oh, C++ exceptions across signals, neat. Didn't think of that.
Other than the cross-arch exceptions, I was thinking of things like using
longjmp for continuations and coroutines, and tricks like that. There may
be clever ways to do such things with C++ exceptions, I don't know then.
Anyway, not wanting to finger-point I was refering to things like
these in the patch:
+ /* 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);
and...
+ if (!what.is_longjmp && ecs->event_thread->step_range_start
+ && (get_frame_sp (get_current_frame ())
+ == ecs->event_thread->exception_frame))
+ {
and...
+ if (!nexting_cfa
+ || gdbarch_inner_than (arch, cfa, nexting_cfa))
+ {
+ /* Not an interesting exception. */
+ break;
+ }
Which I guessed should be using frame_id comparisions, and
something other than gdbarch_inner_than, frame_find_by_id perhaps
(that does require a well behaved unwinder). I'm not much
an expert on C++ unwinding, and haven't really studied the patch,
so I don't exactly what is being compared here.
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 17:47 ` Pedro Alves
@ 2009-06-10 18:32 ` Tom Tromey
2009-06-10 18:49 ` Pedro Alves
2009-06-12 20:49 ` Tom Tromey
1 sibling, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2009-06-10 18:32 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Daniel Jacobowitz, Joel Brobecker
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> Oh, C++ exceptions across signals, neat. Didn't think of that.
Does that work if you specify a signal stack?
I don't know.
Pedro> Other than the cross-arch exceptions, I was thinking of things
Pedro> like using longjmp for continuations and coroutines, and tricks
Pedro> like that. There may be clever ways to do such things with C++
Pedro> exceptions, I don't know then.
I don't think so. But I am prepared to be surprised.
Pedro> Anyway, not wanting to finger-point I was refering to things like
Pedro> these in the patch:
Pedro> + /* We use the current stack pointer and not the CFA here,
Pedro> + because the unwinder seems to compute the callee's CFA, and
Pedro> + so the breakpoint does not trigger. */
Pedro> + stack_ptr = get_frame_sp (frame);
This is bogus, btw, because for the comparison to work properly we
must compute the Dwarf CFA. There's currently no way that I know of
to do this, so presumably some kind of extension to the frame API will
be needed.
Pedro> + if (!nexting_cfa
Pedro> + || gdbarch_inner_than (arch, cfa, nexting_cfa))
Pedro> + {
Pedro> + /* Not an interesting exception. */
Pedro> + break;
Pedro> + }
Pedro> Which I guessed should be using frame_id comparisions, and
Pedro> something other than gdbarch_inner_than, frame_find_by_id perhaps
Pedro> (that does require a well behaved unwinder). I'm not much
Pedro> an expert on C++ unwinding, and haven't really studied the patch,
Pedro> so I don't exactly what is being compared here.
The intent is to compare the "next"ing frame with the target frame of
the exception. Exceptions throw up to or past the nexting frame
should be caught be gdb.
I don't see how this works for longjmp. From the code it looks like
any longjmp is caught... is that the case? I assume I'm missing
something, would you mind point it out?
I suppose the failing case for exception unwinding is if the inferior
stops in a signal handler on a separate stack, then the user "next"s
over a throw in that handler. Does that sound correct?
Thanks for the hint about frame_id comparisons. I will look into
that.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 18:32 ` Tom Tromey
@ 2009-06-10 18:49 ` Pedro Alves
0 siblings, 0 replies; 40+ messages in thread
From: Pedro Alves @ 2009-06-10 18:49 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches, Daniel Jacobowitz, Joel Brobecker
On Wednesday 10 June 2009 19:29:23, Tom Tromey wrote:
> The intent is to compare the "next"ing frame with the target frame of
> the exception. Exceptions throw up to or past the nexting frame
> should be caught be gdb.
Right, this is the bit where comparing SP or FP pointers directly
bites back (other than it is not arch portable to address SP or FP
directly). The simplest replacement is to check if the "next"ing frame
is still in the frame chain (with frame_find_by_id). If it is, then you
know that the target frame is inner to the "next"ing frame. If something
goes wrong with gdb's unwinding, you'll get a false positive and just stop
the inferior in an inner frame (thus, the back case isn't runaway
process, but spurious stop).
> I don't see how this works for longjmp. From the code it looks like
> any longjmp is caught... is that the case?
Yes, that's the case.
> I suppose the failing case for exception unwinding is if the inferior
> stops in a signal handler on a separate stack, then the user "next"s
> over a throw in that handler. Does that sound correct?
Yes. Or, if you "next" in base code, and a signal (set to "pass nostop")
is caught in the meantime, the inferiors switches to an altstack, and then
the signal handler in the inferior throws.
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 17:47 ` Pedro Alves
2009-06-10 18:32 ` Tom Tromey
@ 2009-06-12 20:49 ` Tom Tromey
2009-06-12 21:51 ` Pedro Alves
1 sibling, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2009-06-12 20:49 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches, Daniel Jacobowitz, Joel Brobecker
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> Which I guessed should be using frame_id comparisions, and
Pedro> something other than gdbarch_inner_than, frame_find_by_id
Pedro> perhaps (that does require a well behaved unwinder).
Here is an updated version of the patch.
This version uses frame_id comparisons, and it avoids the unwinder's
notion of the CFA. This means it is a bit less efficient (gdb must do
more work for every exception that is thrown), but also more robust in
the event of odd things, like exceptions from signal handlers.
Joel, I did look at the breakpoint_ops stuff a little, but I don't see
how it could really work for this situation. This really needs some
hooks into handle_inferior_event, and it didn't seem to me that any of
the breakpoint_ops methods were appropriate for this.
Note that this implementation also suffers from a bug affecting
longjmp, http://sourceware.org/bugzilla/show_bug.cgi?id=10236
I still haven't tried it with throw-from-signal-handler; I'll build a
new libgcj soon and try it there. Jakub also asked me to run a few
tests which I have not tried yet. So, this patch is not check-in
ready. Still, I would appreciate any comments you might have.
FWIW, I think the longjmp code ought to work the same way that this
code works. That is, record the initiating frame for an operation,
and ignore lonjmps whose target frame is newer than the initiating
frame. Users wanting the current longjmp behavior could easily
recover it by setting a breakpoint manually.
If this seems like an acceptable plan, I will change longjmp to work
the same way. Let me know what you think.
Tom
2009-06-12 Tom Tromey <tromey@redhat.com>
PR c++/9593:
* 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) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume>:
New constants.
(struct bpstat_what) <is_longjmp>: 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) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Call set_exception_breakpoint.
2009-06-12 Tom Tromey <tromey@redhat.com>
* 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..f02979e 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,39 @@ 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. Returns the frame id corresponding to FRAME, or the
+ null frame id if no breakpoint was set. */
+
+struct frame_id
+set_exception_breakpoint (struct frame_info *frame)
+{
+ struct minimal_symbol *debug_hook;
+ struct frame_id result = null_frame_id;
+
+ debug_hook = lookup_minimal_symbol_text ("_Unwind_DebugHook", NULL);
+ if (debug_hook != NULL)
+ {
+ CORE_ADDR pc;
+
+ 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);
+ result = get_frame_id (frame);
+ }
+
+ return result;
+}
+
static void
create_overlay_event_breakpoint_1 (char *func_name, struct objfile *objfile)
{
@@ -5158,6 +5199,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 +6399,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 +6414,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 +6427,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 +6465,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 +6477,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->initiating_frame = set_exception_breakpoint (prev_frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -6443,6 +6497,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 +7832,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..bdbe3cc 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 struct frame_id 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..9a66760 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -1,6 +1,6 @@
/* Multi-process/thread control defs for GDB, the GNU debugger.
Copyright (C) 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1997, 1998, 1999,
- 2000, 2007, 2008 Free Software Foundation, Inc.
+ 2000, 2007, 2008, 2009 Free Software Foundation, Inc.
Contributed by Lynx Real-Time Systems, Inc. Los Gatos, CA.
@@ -171,6 +171,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
};
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 50e8fff..7e60d0b 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->initiating_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->initiating_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->initiating_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..4686606 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,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
delete_step_resume_breakpoint (ecs->event_thread);
+ if (!what.is_longjmp)
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame.
+
+ 2.1. If we are stepping, defer to the stepping logic.
+
+ 2.2. Otherwise, we are not stepping, so we are doing a
+ "finish" and we have reached the calling frame. So,
+ stop.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ if (ecs->event_thread->step_range_start)
+ {
+ /* Case 2.1. */
+ break;
+ }
+ else
+ {
+ /* Case 2.2: fall through. */
+ }
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+ }
+
ecs->event_thread->stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
@@ -3851,6 +3912,95 @@ 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 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 (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)
{
@@ -3917,6 +4067,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 <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1()
+ {
+ throw 20;
+ }
+
+ // Throw an exception in another function.
+ void function2()
+ {
+ function1();
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 ()
+ {
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow ()
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish ()
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 ();
+ }
+ catch (int x)
+ {
+ }
+ function1 ();
+ }
+
+ 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 <http://www.gnu.org/licenses/>.
+
+
+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"
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-06-12 20:49 ` Tom Tromey
@ 2009-06-12 21:51 ` Pedro Alves
2009-06-12 22:07 ` Pedro Alves
0 siblings, 1 reply; 40+ messages in thread
From: Pedro Alves @ 2009-06-12 21:51 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches, Daniel Jacobowitz, Joel Brobecker
[Reviewing bits and pieces instead of the whole patch at once]
On Friday 12 June 2009 21:44:30, Tom Tromey wrote:
> +/* A continuation callback for until_next_command. */
> +
> +static void
> +until_next_continuation (void *arg)
> +{
> + struct thread_info *tp = arg;
> + delete_longjmp_breakpoint (tp->num);
This is broken, in that there's no guarantee that TP is still a
valid pointer here.
> +}
> +
> /* 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->initiating_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);
In sync execution mode, between that `proceed' and this do_cleanups, a
lot happens. TP may exit, or the whole process for the matter.
So, after `proceed', any TP pointer is invalid. Any other code
doing the same thing is equally broken.
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-06-12 21:51 ` Pedro Alves
@ 2009-06-12 22:07 ` Pedro Alves
0 siblings, 0 replies; 40+ messages in thread
From: Pedro Alves @ 2009-06-12 22:07 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey, Daniel Jacobowitz, Joel Brobecker
On Friday 12 June 2009 22:52:52, Pedro Alves wrote:
> [Reviewing bits and pieces instead of the whole patch at once]
And I'll stop here, get some sleep, and then get back ...
>
> On Friday 12 June 2009 21:44:30, Tom Tromey wrote:
> > +/* A continuation callback for until_next_command. */
> > +
> > +static void
> > +until_next_continuation (void *arg)
> > +{
> > + struct thread_info *tp = arg;
> > + delete_longjmp_breakpoint (tp->num);
>
> This is broken, in that there's no guarantee that TP is still a
> valid pointer here.
Somehow, I read this as being the cleanup that the do_cleanups
call below runs...
>
> > +}
> > +
> > /* 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->initiating_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);
>
> In sync execution mode, between that `proceed' and this do_cleanups, a
> lot happens. TP may exit, or the whole process for the matter.
> So, after `proceed', any TP pointer is invalid. Any other code
> doing the same thing is equally broken.
>
This principle is valid, but your code is actually fine, because
TP continuations are always run with TP being valid.
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 17:13 ` Daniel Jacobowitz
2009-06-10 17:47 ` Pedro Alves
@ 2010-11-25 4:54 ` Jan Kratochvil
1 sibling, 0 replies; 40+ messages in thread
From: Jan Kratochvil @ 2010-11-25 4:54 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: Pedro Alves, gdb-patches, tromey, Joel Brobecker
On Wed, 10 Jun 2009 19:13:28 +0200, Daniel Jacobowitz wrote:
[...]
> exceptions can be thrown through
> signal handlers on many platforms; so yes, they might switch stack.
On Fedora 14 x86_64 getting instead of a caught exception-from-sighandler:
terminate called after throwing an instance of 'int'
from the testcase below and ISO C++ says:
The common subset of the C and C++ languages consists of all
declarations, definitions, and expressions that may appear in a well
formed C++ program and also in a conforming C program. A POF (“plain
old function”) is a function that uses only features from this common
subset, and that does not directly or indirectly use any function that
is not a POF, except that it may use functions defined in Clause 29
that are not member functions. All signal handlers shall have
C linkage. A POF that could be used as a signal handler in
a conforming C program does not produce undefined behavior when used
as a signal handler in a C++ program. The behavior of any other
function used as a signal handler in a C++ program is
implementation-defined.228
228) In particular, a signal handler using exception handling is very
-------------------------------------------------
likely to have problems. Also, invoking std::exit may cause
-----------------------
destruction of objects, including those of the standard library
implementation, which, in general, yields undefined behavior in
a signal handler (see 1.9).
If this really does not have to work it means for GDB the sigaltstack case is
unrelated to this "next/finish/etc -vs- exceptions" patch.
Thanks,
Jan
#include <signal.h>
#include <assert.h>
#include <iostream>
using namespace std;
static void
handler (int signo)
{
throw 1;
}
int
main (void)
{
sighandler_t sigvar;
sigvar = signal (SIGUSR1, handler);
assert (sigvar == SIG_DFL);
try
{
int i;
i = raise (SIGUSR1);
assert (i == 0);
}
catch (...)
{
cout << "caught" << endl;
}
cout << "done" << endl;
}
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-10 16:50 ` Tom Tromey
2009-06-10 17:05 ` Pedro Alves
@ 2009-06-11 14:45 ` Joel Brobecker
2009-06-11 15:47 ` Tom Tromey
1 sibling, 1 reply; 40+ messages in thread
From: Joel Brobecker @ 2009-06-11 14:45 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
> It would be nice if we had a flag day and simply converted everything
> to breakpoint_ops. That's usually the best way to ensure that new
> code is written the "right" way (gcc has also had some bad experiences
> with these incomplete transitions).
I tried to convert a few of them a few months back. The issue is
that it's not always trivial, and sometimes the breakpoint_ops model
doesn't fit well for the functionality that you're trying to provide.
That's why I was asking whether you had a look at it, because I am
not sure that it will work. But if it does, it really simplifies
greatly the patch, or at least greatly reduces the number of places
where you touch the code.
> I was under the impression that Ada used longjmp to implement
> exceptions, and so the existing longjmp support should work. (Modulo
> the odd bug or two -- I'm not sure if Pedro's fix for the glibc
> pointer mangling went in or not.)
Actually, we have two possible mechanisms, but I think we prefer the
"Zero Cost Exception" mechanism, which doesn't use setjmp/longjmp
because having an exception handler then costs nothing. We assume
that exceptions are rare occurences, and therefore don't wan't to
spent time setting up a handler if we're not going to use it.
--
Joel
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-06-11 14:45 ` Joel Brobecker
@ 2009-06-11 15:47 ` Tom Tromey
0 siblings, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2009-06-11 15:47 UTC (permalink / raw)
To: Joel Brobecker; +Cc: gdb-patches
>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:
Joel> Actually, we have two possible mechanisms, but I think we prefer the
Joel> "Zero Cost Exception" mechanism, which doesn't use setjmp/longjmp
Joel> because having an exception handler then costs nothing. We assume
Joel> that exceptions are rare occurences, and therefore don't wan't to
Joel> spent time setting up a handler if we're not going to use it.
Ok, great. If Ada uses the same unwinder code as the rest of gcc --
in particular, unwind-dw2.c -- then this patch will "just work" for
Ada as it does for Java and C++. That is because the debug hook in
the unwinder is inserted at the very lowest level.
Note that I have not updated all the unwinders. In particular this
will not work on IA-64, on any platform using unwind-sjlj.c (but those
should be covered by the ordinary longjmp code), or I think on Xtensa
or ARM (based on the existence of unwind-$arch.c in the gcc tree).
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-05-29 22:32 RFC: next/finish/etc -vs- exceptions Tom Tromey
2009-05-30 21:08 ` Doug Evans
2009-06-10 16:13 ` Joel Brobecker
@ 2009-07-24 17:38 ` Tom Tromey
2009-07-24 18:54 ` Pedro Alves
2 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2009-07-24 17:38 UTC (permalink / raw)
To: gdb-patches
Here is a new revision of this patch.
I addressed most of the comments made about the earlier versions. It
now uses frame ids and it includes a test for a throw from a signal
handler (written in Java, since that was simplest).
I didn't address Doug's comment about issuing a warning when the needed
debug hook cannot be found. It seems strange to do this in general,
because exceptions are not used by all programs.
I also haven't yet tried to make the longjmp code work like the
exception code. I'd like to see Pedro's glibc/longjmp fix go in first,
so I can have a reasonable chance of testing any changes here.
In order to use this new feature you must use a new gcc -- one that has
the necessary debug function in the unwinder.
Built and regtested on x86-64 (compile farm). This compiler doesn't
have the new hook, so I also tested it locally (x86 F11) using a svn
trunk gcc.
Let me know what you think.
Tom
2009-07-24 Tom Tromey <tromey@redhat.com>
PR c++/9593:
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(insert_exception_resume_breakpoint): New function.
(check_exception_resume): Likewise.
(keep_going): Print exception when debugging.
* inferior.h: (delete_longjmp_breakpoint_cleanup): Declare.
* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
(step_1): Set initiating_frame.
(until_next_continuation): New function.
(until_next_command): Set initiating_frame. Make a continuation.
(finish_command_continuation): Call delete_longjmp_breakpoint.
(finish_forward): Set initiating_frame.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
bp_exception_master>: New constants.
(struct bpstat_what) <is_longjmp>: New field.
* breakpoint.c (update_breakpoints_after_exec): Handle
bp_exception, bp_exception_resume, and bp_exception_master. Call
create_exception_master_breakpoint.
(print_it_typical): Handle bp_exception, bp_exception_resume, and
bp_exception_master.
(bpstat_stop_status): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(mention): Likewise.
(breakpoint_re_set_one): Likewise.
(delete_command): Likewise.
(bpstat_what): Likewise. Set 'is_longjmp' field.
(set_longjmp_breakpoint): Handle bp_exception_master.
(delete_longjmp_breakpoint): Handle bp_exception.
(create_exception_master_breakpoint): New function.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Call set_exception_breakpoint.
(breakpoint_re_set): Call create_exception_master_breakpoint.
2009-07-24 Tom Tromey <tromey@redhat.com>
* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* 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 3a18c8f..621e527 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -1548,6 +1548,36 @@ create_longjmp_master_breakpoint (char *func_name)
update_global_location_list (1);
}
+/* 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)
+ {
+ CORE_ADDR pc;
+ struct breakpoint *b;
+
+ pc = find_function_start_pc (get_objfile_arch (objfile),
+ SYMBOL_VALUE_ADDRESS (debug_hook),
+ SYMBOL_OBJ_SECTION (debug_hook));
+ b = create_internal_breakpoint (get_objfile_arch (objfile),
+ pc, 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)
{
@@ -1578,7 +1608,7 @@ 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_longjmp_master || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
@@ -1593,7 +1623,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;
@@ -1654,6 +1685,7 @@ update_breakpoints_after_exec (void)
create_longjmp_master_breakpoint ("_longjmp");
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
+ create_exception_master_breakpoint ();
}
int
@@ -2482,6 +2514,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);
@@ -2569,6 +2607,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:
@@ -3178,7 +3218,7 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid)
continue;
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master)
+ || b->type == bp_longjmp_master || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
@@ -3394,6 +3434,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;
@@ -3440,10 +3481,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)
@@ -3463,6 +3509,7 @@ bpstat_what (bpstat bs)
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
+ case bp_exception_master:
bs_class = bp_nostop;
break;
case bp_catchpoint:
@@ -3584,6 +3631,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"},
@@ -3591,6 +3640,7 @@ print_one_breakpoint_location (struct breakpoint *b,
{bp_thread_event, "thread events"},
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
};
@@ -3713,6 +3763,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:
@@ -3720,6 +3772,7 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
+ case bp_exception_master:
case bp_tracepoint:
if (opts.addressprint)
{
@@ -4356,6 +4409,8 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4363,6 +4418,7 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -4540,8 +4596,7 @@ make_breakpoint_permanent (struct breakpoint *b)
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in THREAD. */
void
set_longjmp_breakpoint (int thread)
@@ -4553,10 +4608,10 @@ set_longjmp_breakpoint (int thread)
longjmp "master" breakpoints. Here, we simply create momentary
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp_master)
+ if (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;
}
}
@@ -4568,7 +4623,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);
@@ -5273,6 +5328,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:
@@ -5280,6 +5337,7 @@ mention (struct breakpoint *b)
case bp_thread_event:
case bp_overlay_event:
case bp_longjmp_master:
+ case bp_exception_master:
break;
}
@@ -6572,6 +6630,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
@@ -6586,6 +6645,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
@@ -6597,6 +6657,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 ();
@@ -6635,6 +6697,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. */
@@ -6647,6 +6712,10 @@ until_break_command (char *arg, int from_tty, int anywhere)
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = frame_unwind_caller_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -6663,6 +6732,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 (),
@@ -7588,6 +7658,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
@@ -7606,6 +7677,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_thread_event
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
@@ -7904,6 +7976,7 @@ breakpoint_re_set_one (void *bint)
reset later by breakpoint_re_set. */
case bp_overlay_event:
case bp_longjmp_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
@@ -7926,6 +7999,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;
}
@@ -7959,6 +8034,7 @@ breakpoint_re_set (void)
create_longjmp_master_breakpoint ("_longjmp");
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
+ create_exception_master_breakpoint ();
}
\f
/* Reset the thread number of this breakpoint:
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 6731e68..86afc64 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -55,6 +55,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,
@@ -117,6 +124,9 @@ enum bptype
bp_longjmp_master,
+ /* Like bp_longjmp_master, but for exceptions. */
+ bp_exception_master,
+
bp_catchpoint,
bp_tracepoint,
@@ -561,6 +571,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,
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index 79d33fe..4f76dc2 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -185,6 +185,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
};
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 9e98290..fab1892 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -801,7 +801,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;
@@ -841,10 +841,13 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
if (!single_inst || skip_subroutines) /* leave si command alone */
{
+ struct thread_info *tp = inferior_thread ();
+
if (in_thread_list (inferior_ptid))
thread = pid_to_thread_id (inferior_ptid);
set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (get_current_frame ());
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1193,6 +1196,15 @@ signal_command (char *signum_exp, int from_tty)
proceed ((CORE_ADDR) -1, oursig, 0);
}
+/* A continuation callback for until_next_command. */
+
+static void
+until_next_continuation (void *arg)
+{
+ struct thread_info *tp = arg;
+ delete_longjmp_breakpoint (tp->num);
+}
+
/* Proceed until we reach a different source line with pc greater than
our current one or exit the function. We skip calls in both cases.
@@ -1209,6 +1221,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 ();
@@ -1244,7 +1258,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
@@ -1421,6 +1447,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
@@ -1504,6 +1531,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);
@@ -1514,6 +1542,10 @@ finish_forward (struct symbol *function, struct frame_info *frame)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0);
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 7312e51..d4eae88 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -272,6 +272,8 @@ extern void interrupt_target_command (char *args, int from_tty);
extern void interrupt_target_1 (int all_threads);
+extern void delete_longjmp_breakpoint_cleanup (void *arg);
+
extern void detach_command (char *, int);
extern void notice_new_inferior (ptid_t, int, int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 9f6cfc9..bccc00c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -45,6 +45,8 @@
#include "language.h"
#include "solib.h"
#include "main.h"
+#include "dictionary.h"
+#include "block.h"
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
@@ -1768,6 +1770,8 @@ static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void check_exception_resume (struct execution_control_state *,
+ struct frame_info *, struct symbol *);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
@@ -3405,23 +3409,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;
@@ -3433,6 +3447,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
delete_step_resume_breakpoint (ecs->event_thread);
+ if (!what.is_longjmp)
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame.
+
+ 2.1. If we are stepping, defer to the stepping logic.
+
+ 2.2. Otherwise, we are not stepping, so we are doing a
+ "finish" and we have reached the calling frame. So,
+ stop.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ if (ecs->event_thread->step_range_start)
+ {
+ /* Case 2.1. */
+ break;
+ }
+ else
+ {
+ /* Case 2.2: fall through. */
+ }
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+ }
+
ecs->event_thread->stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
stop_stepping (ecs);
@@ -4377,6 +4438,96 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
+/* Insert an exception resume breakpoint. TP is the thread throwing
+ the exception. The block B is the block of the unwinder debug hook
+ function. FRAME is the frame corresponding to the call to this
+ function. SYM is the symbol of the function argument holding the
+ target PC of the exception. */
+
+static void
+insert_exception_resume_breakpoint (struct thread_info *tp,
+ struct block *b,
+ struct frame_info *frame,
+ struct symbol *sym)
+{
+ struct gdb_exception e;
+
+ /* We want to ignore errors here. */
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ {
+ struct symbol *vsym;
+ struct value *value;
+ CORE_ADDR handler;
+ struct breakpoint *bp;
+
+ vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+ value = read_var_value (vsym, frame);
+ 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)
{
@@ -4445,6 +4596,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 0a087c7..b4880f2 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 pr9594 gdb2495
+ ref-types ref-params method2 pr9594 gdb2495 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..783c962
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.cc
@@ -0,0 +1,180 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008, 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1()
+ {
+ throw 20;
+ }
+
+ // Throw an exception in another function.
+ void function2()
+ {
+ function1();
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 ()
+ {
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow ()
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish ()
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 ();
+ }
+ catch (int x)
+ {
+ }
+ function1 (); // marker for until
+ }
+
+ void until ()
+ {
+ function1 ();
+ function1 (); // until here
+ }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main ()
+{
+ try
+ {
+ next_cases.function1 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // This is duplicated so we can next over one but step into
+ // another.
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ next_cases.function3 ();
+
+ try
+ {
+ next_cases.rethrow ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // Another duplicate so we can test "finish".
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ // Another test for "finish".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until" with an argument.
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "advance".
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+}
+
diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
new file mode 100644
index 0000000..ee9aeff
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.exp
@@ -0,0 +1,185 @@
+# Copyright 2008, 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+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"
+
+set line [gdb_get_line_number "marker for until" $testfile.cc]
+
+gdb_test "until $line" \
+ ".*function1 ().*" \
+ "next past catch 6"
+
+gdb_test "until" \
+ ".*catch (...).*" \
+ "until with no argument 2"
+
+set line [gdb_get_line_number "until here" $testfile.cc]
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 6"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until"
+
+gdb_test "until $line" \
+ ".*catch (...).*" \
+ "until-over-throw"
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 7"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until, for advance"
+
+gdb_test "advance $line" \
+ ".*catch (...).*" \
+ "advance-over-throw"
diff --git a/gdb/testsuite/gdb.java/jnpe.exp b/gdb/testsuite/gdb.java/jnpe.exp
new file mode 100644
index 0000000..74d4d58
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.exp
@@ -0,0 +1,72 @@
+# Copyright 2009 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+load_lib "java.exp"
+
+set testfile "jnpe"
+set srcfile ${srcdir}/$subdir/${testfile}.java
+set binfile ${objdir}/${subdir}/${testfile}
+if { [compile_java_from_source ${srcfile} ${binfile} "-g"] != "" } {
+ untested "Couldn't compile ${srcfile}"
+ return -1
+}
+
+set prms_id 0
+set bug_id 0
+
+# 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" \
+ "Current language.*java" \
+ "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.
+ pass "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"
+
+# We sometimes stop at line 37, not line 35. This seems to be a gcj
+# oddity -- another next will solve it.
+gdb_test "next" \
+ "3\[57\].*" \
+ "next over NPE"
diff --git a/gdb/testsuite/gdb.java/jnpe.java b/gdb/testsuite/gdb.java/jnpe.java
new file mode 100644
index 0000000..ffca3ab
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.java
@@ -0,0 +1,38 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class jnpe
+{
+ public static String npe ()
+ {
+ return ((Object) null).toString();
+ }
+
+ public static void main (String[] args)
+ {
+ try
+ {
+ System.out.println (npe ()); // break here
+ }
+ catch (NullPointerException n)
+ {
+ System.out.println ("success");
+ }
+ }
+}
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2009-07-24 17:38 ` Tom Tromey
@ 2009-07-24 18:54 ` Pedro Alves
2009-07-24 19:18 ` Tom Tromey
2009-09-09 18:56 ` Tom Tromey
0 siblings, 2 replies; 40+ messages in thread
From: Pedro Alves @ 2009-07-24 18:54 UTC (permalink / raw)
To: gdb-patches, Tom Tromey
On Friday 24 July 2009 17:54:50, Tom Tromey wrote:
> I also haven't yet tried to make the longjmp code work like the
> exception code. I'd like to see Pedro's glibc/longjmp fix go in first,
> so I can have a reasonable chance of testing any changes here.
Sorry, I took a deeper look at your previous patch a few weeks
ago over a weekend, but then the weekend ran out before I finished
the review, and then I stayed quiet about it waiting for some other
time to finish what I had started, but that never arrived. :-/
I already had patches to do something like like you mention to
the longjmp code from 2008, but I hadn't re-posted them because
of the glibc pointer mangling getting in the way.
I found out that your code to detect if the exception is outer or
not won't always work for longjmps, in the longjmp tests I have
for example. This made me wonder if we shouldn't get longjmp
done first, and then maybe reuse it or not for exceptions if
it makes sense. As it is in your patch, you're reusing the
longjmp paths in infrun.c and co., but it may end up that's
not a good choice.
I also thought at the time that there were some things in that
patch (I didn't look at this new one yet), that should be split
into independent changes, like changes to insert longjmp breakpoints
in a few commands that didn't had them inserted (but memory is a
bit vague by now though, I can't remember exact details). I did brush
up my only-follow-longjmp-if-going-outer patches a bit and I was
aiming at posting it before you had updated your patch, but obviously
I failed. :-/
I think if we have a chance of looking at what needs addressing
for longjmp first (and split your changes that concern with longjmp
too), we will have a better result. Would you mind that? I'll try to
get at it again this weekend, and post what I have, and finish up
the review I had already started (looking at the new patch, of course).
--
Pedro Alves
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-07-24 18:54 ` Pedro Alves
@ 2009-07-24 19:18 ` Tom Tromey
2009-09-09 18:56 ` Tom Tromey
1 sibling, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2009-07-24 19:18 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Pedro> As it is in your patch, you're reusing the longjmp paths in
Pedro> infrun.c and co., but it may end up that's not a good choice.
Yeah, I wondered about that. I did it this way because the user
functionality is really similar; but if the implementation needs diverge
then it is not a good approach.
Pedro> I also thought at the time that there were some things in that
Pedro> patch (I didn't look at this new one yet), that should be split
Pedro> into independent changes, like changes to insert longjmp breakpoints
Pedro> in a few commands that didn't had them inserted (but memory is a
Pedro> bit vague by now though, I can't remember exact details).
Yes, that would make sense. I can do that. (And, your recollection is
correct here.)
Pedro> I did brush
Pedro> up my only-follow-longjmp-if-going-outer patches a bit and I was
Pedro> aiming at posting it before you had updated your patch, but obviously
Pedro> I failed. :-/
No problem!
Pedro> I think if we have a chance of looking at what needs addressing
Pedro> for longjmp first (and split your changes that concern with longjmp
Pedro> too), we will have a better result. Would you mind that?
Not at all. I'll wait to see that and then see what changes and/or
splits my patch needs.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2009-07-24 18:54 ` Pedro Alves
2009-07-24 19:18 ` Tom Tromey
@ 2009-09-09 18:56 ` Tom Tromey
1 sibling, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2009-09-09 18:56 UTC (permalink / raw)
To: Pedro Alves; +Cc: gdb-patches
>>>>> "Pedro" == Pedro Alves <pedro@codesourcery.com> writes:
Tom> I also haven't yet tried to make the longjmp code work like the
Tom> exception code. I'd like to see Pedro's glibc/longjmp fix go in first,
Tom> so I can have a reasonable chance of testing any changes here.
[...]
Pedro> I think if we have a chance of looking at what needs addressing
Pedro> for longjmp first (and split your changes that concern with longjmp
Pedro> too), we will have a better result. Would you mind that? I'll try to
Pedro> get at it again this weekend, and post what I have, and finish up
Pedro> the review I had already started (looking at the new patch, of course).
Just a friendly reminder... I think this is still blocked, pending your
longjmp patch.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* RFC: next/finish/etc -vs- exceptions
@ 2010-10-07 1:37 Tom Tromey
2010-11-24 17:53 ` Joel Brobecker
` (2 more replies)
0 siblings, 3 replies; 40+ messages in thread
From: Tom Tromey @ 2010-10-07 1:37 UTC (permalink / raw)
To: gdb-patches
This is a refresh of:
http://www.sourceware.org/ml/gdb-patches/2009-05/msg00635.html
(The thread continues in the following month.)
I rebased the patch and addressed Pedro's comments.
I did not add the user notification that Doug asked for.
This version does work if an exception is thrown from a signal handler.
The included Java test case checks that (on most platforms).
Built and regtested on x86-64 (compile farm).
Tests for the new functionality are included.
I'd appreciate comments. I don't want to commit this without a review,
as my experience is that I usually err when touching breakpoints or
infrun.
If you want to try this yourself, you will need a new-enough libgcc, one
that includes the necessary debugging hook.
Tom
2010-10-06 Tom Tromey <tromey@redhat.com>
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(handle_inferior_event): Likewise.
(insert_exception_resume_breakpoint): New function.
(check_exception_resume): Likewise.
* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
(step_1): Set thread's initiating frame.
(until_next_continuation): New function.
(until_next_command): Support exception breakpoints.
(finish_command_continuation): Delete longjmp breakpoint.
(finish_forward): Support exception breakpoints.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
bp_exception_master>: New constants.
(struct bpstat_what) <is_longjmp>: New field.
* breakpoint.c (create_exception_master_breakpoint): New function.
(update_breakpoints_after_exec): Handle bp_exception_master. Call
create_exception_master_breakpoint.
(print_it_typical): Handle bp_exception_master, bp_exception.
(bpstat_stop_status): Handle bp_exception_master.
(bpstat_what): Handle bp_exception_master, bp_exception,
bp_exception_resume.
(bptype_string): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(set_longjmp_breakpoint): Handle exception breakpoints.
(delete_longjmp_breakpoint): Likewise.
(mention): Likewise.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Support exception breakpoints.
(delete_command): Likewise.
(breakpoint_re_set_one): Likewise.
(breakpoint_re_set): Likewise.
2010-10-06 Tom Tromey <tromey@redhat.com>
* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* gdb.cp/gdb9593.exp: New file.
* gdb.cp/gdb9593.cc: New file.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index b4502e7..345556c 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2191,6 +2191,33 @@ create_std_terminate_master_breakpoint (const char *func_name)
do_cleanups (old_chain);
}
+/* Install a master breakpoint on the unwinder's debug hook. */
+
+void
+create_exception_master_breakpoint (void)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *debug_hook;
+
+ debug_hook = lookup_minimal_symbol_text ("_Unwind_DebugHook", objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+
+ b = create_internal_breakpoint (get_objfile_arch (objfile),
+ SYMBOL_VALUE_ADDRESS (debug_hook),
+ bp_exception_master);
+ b->addr_string = xstrdup ("_Unwind_DebugHook");
+ b->enable_state = bp_disabled;
+ }
+ }
+
+ update_global_location_list (1);
+}
+
void
update_breakpoints_after_exec (void)
{
@@ -2232,7 +2259,8 @@ update_breakpoints_after_exec (void)
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master || b->type == bp_std_terminate_master)
+ || b->type == bp_longjmp_master || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
@@ -2247,7 +2275,8 @@ update_breakpoints_after_exec (void)
/* Longjmp and longjmp-resume breakpoints are also meaningless
after an exec. */
- if (b->type == bp_longjmp || b->type == bp_longjmp_resume)
+ if (b->type == bp_longjmp || b->type == bp_longjmp_resume
+ || b->type == bp_exception || b->type == bp_exception_resume)
{
delete_breakpoint (b);
continue;
@@ -2309,6 +2338,7 @@ update_breakpoints_after_exec (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
int
@@ -3230,6 +3260,12 @@ print_it_typical (bpstat bs)
result = PRINT_NOTHING;
break;
+ case bp_exception_master:
+ /* These should never be enabled. */
+ printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
+ result = PRINT_NOTHING;
+ break;
+
case bp_watchpoint:
case bp_hardware_watchpoint:
annotate_watchpoint (b->number);
@@ -3317,6 +3353,8 @@ print_it_typical (bpstat bs)
case bp_none:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4103,7 +4141,8 @@ bpstat_stop_status (struct address_space *aspace,
if (b->type == bp_thread_event || b->type == bp_overlay_event
|| b->type == bp_longjmp_master
- || b->type == bp_std_terminate_master)
+ || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
@@ -4198,6 +4237,7 @@ bpstat_what (bpstat bs)
retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;
for (; bs != NULL; bs = bs->next)
{
@@ -4253,10 +4293,14 @@ bpstat_what (bpstat bs)
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -4272,6 +4316,7 @@ bpstat_what (bpstat bs)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
this_action = BPSTAT_WHAT_SINGLE;
break;
case bp_catchpoint:
@@ -4465,6 +4510,8 @@ bptype_string (enum bptype type)
{bp_access_watchpoint, "acc watchpoint"},
{bp_longjmp, "longjmp"},
{bp_longjmp_resume, "longjmp resume"},
+ {bp_exception, "exception"},
+ {bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
@@ -4474,6 +4521,7 @@ bptype_string (enum bptype type)
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
{bp_std_terminate_master, "std::terminate master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
{bp_fast_tracepoint, "fast tracepoint"},
@@ -4612,6 +4660,8 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4621,6 +4671,7 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
case bp_tracepoint:
case bp_fast_tracepoint:
case bp_static_tracepoint:
@@ -5359,6 +5410,8 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -5369,6 +5422,7 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -5584,8 +5638,7 @@ make_breakpoint_permanent (struct breakpoint *b)
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in THREAD. */
void
set_longjmp_breakpoint (int thread)
@@ -5598,11 +5651,12 @@ set_longjmp_breakpoint (int thread)
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
if (b->pspace == current_program_space
- && b->type == bp_longjmp_master)
+ && (b->type == bp_longjmp_master
+ || b->type == bp_exception_master))
{
struct breakpoint *clone = clone_momentary_breakpoint (b);
- clone->type = bp_longjmp;
+ clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
clone->thread = thread;
}
}
@@ -5614,7 +5668,7 @@ delete_longjmp_breakpoint (int thread)
struct breakpoint *b, *temp;
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp)
+ if (b->type == bp_longjmp || b->type == bp_exception)
{
if (b->thread == thread)
delete_breakpoint (b);
@@ -6786,6 +6840,8 @@ mention (struct breakpoint *b)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
@@ -6796,6 +6852,7 @@ mention (struct breakpoint *b)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}
@@ -8449,6 +8506,7 @@ struct until_break_command_continuation_args
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};
/* This function is called by fetch_inferior_event via the
@@ -8463,6 +8521,7 @@ until_break_command_continuation (void *arg)
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}
void
@@ -8474,6 +8533,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;
clear_proceed_status ();
@@ -8512,6 +8573,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ tp = inferior_thread ();
+ thread = tp->num;
+
/* Keep within the current frame, or in frames called by the current
one. */
@@ -8524,6 +8588,10 @@ until_break_command (char *arg, int from_tty, int anywhere)
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = frame_unwind_caller_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8540,6 +8608,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
@@ -9778,6 +9847,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
@@ -9799,6 +9869,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
@@ -10259,6 +10330,7 @@ breakpoint_re_set_one (void *bint)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
@@ -10282,6 +10354,8 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_jit_event:
break;
}
@@ -10325,6 +10399,7 @@ breakpoint_re_set (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
\f
/* Reset the thread number of this breakpoint:
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 9f7600a..4f1163f 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -56,6 +56,13 @@ enum bptype
bp_longjmp, /* secret breakpoint to find longjmp() */
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
+ /* An internal breakpoint that is installed on the unwinder's
+ debug hook. */
+ bp_exception,
+ /* An internal breakpoint that is set at the point where an
+ exception will land. */
+ bp_exception_resume,
+
/* Used by wait_for_inferior for stepping over subroutine calls, for
stepping over signal handlers, and for skipping prologues. */
bp_step_resume,
@@ -125,6 +132,9 @@ enum bptype
/* Master copies of std::terminate breakpoints. */
bp_std_terminate_master,
+ /* Like bp_longjmp_master, but for exceptions. */
+ bp_exception_master,
+
bp_catchpoint,
bp_tracepoint,
@@ -657,6 +667,10 @@ struct bpstat_what
continuing from a call dummy without popping the frame is not a
useful one). */
enum stop_stack_kind call_dummy;
+
+ /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME. True if we are
+ handling a longjmp, false if we are handling an exception. */
+ int is_longjmp;
};
/* The possible return values for print_bpstat, print_it_normal,
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index cd24eaf..611dcbb 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -185,6 +185,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c4cdb06..d213f6a 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -822,7 +822,7 @@ nexti_command (char *count_string, int from_tty)
step_1 (1, 1, count_string);
}
-static void
+void
delete_longjmp_breakpoint_cleanup (void *arg)
{
int thread = * (int *) arg;
@@ -862,10 +862,13 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
if (!single_inst || skip_subroutines) /* leave si command alone */
{
+ struct thread_info *tp = inferior_thread ();
+
if (in_thread_list (inferior_ptid))
thread = pid_to_thread_id (inferior_ptid);
set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (get_current_frame ());
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1219,6 +1222,15 @@ signal_command (char *signum_exp, int from_tty)
proceed ((CORE_ADDR) -1, oursig, 0);
}
+/* A continuation callback for until_next_command. */
+
+static void
+until_next_continuation (void *arg)
+{
+ struct thread_info *tp = arg;
+ delete_longjmp_breakpoint (tp->num);
+}
+
/* Proceed until we reach a different source line with pc greater than
our current one or exit the function. We skip calls in both cases.
@@ -1235,6 +1247,8 @@ until_next_command (int from_tty)
struct symbol *func;
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
+ int thread = tp->num;
+ struct cleanup *old_chain;
clear_proceed_status ();
set_step_frame ();
@@ -1270,7 +1284,19 @@ until_next_command (int from_tty)
tp->step_multi = 0; /* Only one call to proceed */
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (frame);
+ old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+ if (target_can_async_p () && is_running (inferior_ptid))
+ {
+ discard_cleanups (old_chain);
+ add_continuation (tp, until_next_continuation, tp, NULL);
+ }
+ else
+ do_cleanups (old_chain);
}
static void
@@ -1463,6 +1489,7 @@ finish_command_continuation (void *arg)
if (bs != NULL && tp->proceed_to_finish)
observer_notify_normal_stop (bs, 1 /* print frame */);
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}
static void
@@ -1546,6 +1573,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
struct breakpoint *breakpoint;
struct cleanup *old_chain;
struct finish_command_continuation_args *cargs;
+ int thread = tp->num;
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
@@ -1556,6 +1584,10 @@ finish_forward (struct symbol *function, struct frame_info *frame)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ set_longjmp_breakpoint (thread);
+ tp->initiating_frame = get_frame_id (frame);
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
cargs = xmalloc (sizeof (*cargs));
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 5abec68..e309277 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -291,6 +291,8 @@ extern void interrupt_target_command (char *args, int from_tty);
extern void interrupt_target_1 (int all_threads);
+extern void delete_longjmp_breakpoint_cleanup (void *arg);
+
extern void detach_command (char *, int);
extern void notice_new_inferior (ptid_t, int, int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 0720b31..f75a52f 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -45,6 +45,8 @@
#include "language.h"
#include "solib.h"
#include "main.h"
+#include "dictionary.h"
+#include "block.h"
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
@@ -2209,6 +2211,8 @@ static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void check_exception_resume (struct execution_control_state *,
+ struct frame_info *, struct symbol *);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
@@ -4093,23 +4097,33 @@ process_event_stop_test:
ecs->event_thread->stepping_over_breakpoint = 1;
- if (!gdbarch_get_longjmp_target_p (gdbarch)
- || !gdbarch_get_longjmp_target (gdbarch, frame, &jmp_buf_pc))
+ if (what.is_longjmp)
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
+ if (!gdbarch_get_longjmp_target_p (gdbarch)
+ || !gdbarch_get_longjmp_target (gdbarch,
+ frame, &jmp_buf_pc))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "\
infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
- keep_going (ecs);
- return;
- }
+ keep_going (ecs);
+ return;
+ }
- /* We're going to replace the current step-resume breakpoint
- with a longjmp-resume breakpoint. */
- delete_step_resume_breakpoint (ecs->event_thread);
+ /* We're going to replace the current step-resume breakpoint
+ with a longjmp-resume breakpoint. */
+ delete_step_resume_breakpoint (ecs->event_thread);
- /* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ /* Insert a breakpoint at resume address. */
+ insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ }
+ else
+ {
+ struct symbol *func = get_frame_function (frame);
+ if (func)
+ check_exception_resume (ecs, frame, func);
+ }
keep_going (ecs);
return;
@@ -4121,6 +4135,53 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
delete_step_resume_breakpoint (ecs->event_thread);
+ if (!what.is_longjmp)
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame.
+
+ 2.1. If we are stepping, defer to the stepping logic.
+
+ 2.2. Otherwise, we are not stepping, so we are doing a
+ "finish" and we have reached the calling frame. So,
+ stop.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ if (ecs->event_thread->step_range_start)
+ {
+ /* Case 2.1. */
+ break;
+ }
+ else
+ {
+ /* Case 2.2: fall through. */
+ }
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+ }
+
ecs->event_thread->stop_step = 1;
print_end_stepping_range_reason ();
stop_stepping (ecs);
@@ -5087,6 +5148,100 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
+/* Insert an exception resume breakpoint. TP is the thread throwing
+ the exception. The block B is the block of the unwinder debug hook
+ function. FRAME is the frame corresponding to the call to this
+ function. SYM is the symbol of the function argument holding the
+ target PC of the exception. */
+
+static void
+insert_exception_resume_breakpoint (struct thread_info *tp,
+ struct block *b,
+ struct frame_info *frame,
+ struct symbol *sym)
+{
+ struct gdb_exception e;
+
+ /* We want to ignore errors here. */
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ {
+ struct symbol *vsym;
+ struct value *value;
+ CORE_ADDR handler;
+ struct breakpoint *bp;
+
+ vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+ value = read_var_value (vsym, frame);
+ /* If the value was optimized out, revert to the old behavior. */
+ if (! value_optimized_out (value))
+ {
+ handler = value_as_address (value);
+
+ /* We're going to replace the current step-resume breakpoint
+ with an exception-resume breakpoint. */
+ delete_step_resume_breakpoint (tp);
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: exception resume at %lx\n",
+ (unsigned long) handler);
+
+ bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
+ handler, bp_exception_resume);
+ inferior_thread ()->step_resume_breakpoint = bp;
+ }
+ }
+}
+
+/* This is called when an exception has been intercepted. Check to
+ see whether the exception's destination is of interest, and if so,
+ set an exception resume breakpoint there. */
+
+static void
+check_exception_resume (struct execution_control_state *ecs,
+ struct frame_info *frame, struct symbol *func)
+{
+ struct gdb_exception e;
+
+ TRY_CATCH (e, RETURN_MASK_ALL)
+ {
+ struct block *b;
+ struct dict_iterator iter;
+ struct symbol *sym;
+ int argno = 0;
+
+ /* The exception breakpoint is a thread-specific breakpoint on
+ the unwinder's debug hook, declared as:
+
+ void _Unwind_DebugHook (void *cfa, void *handler);
+
+ The CFA argument indicates the frame to which control is
+ about to be transferred. HANDLER is the destination PC.
+
+ We ignore the CFA and set a temporary breakpoint at HANDLER.
+ This is not extremely efficient but it avoids issues in gdb
+ with computing the DWARF CFA, and it also works even in weird
+ cases such as throwing an exception from inside a signal
+ handler. */
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ ALL_BLOCK_SYMBOLS (b, iter, sym)
+ {
+ if (!SYMBOL_IS_ARGUMENT (sym))
+ continue;
+
+ if (argno == 0)
+ ++argno;
+ else
+ {
+ insert_exception_resume_breakpoint (ecs->event_thread,
+ b, frame, sym);
+ break;
+ }
+ }
+ }
+}
+
static void
stop_stepping (struct execution_control_state *ecs)
{
diff --git a/gdb/testsuite/gdb.cp/gdb9593.cc b/gdb/testsuite/gdb.cp/gdb9593.cc
new file mode 100644
index 0000000..783c962
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.cc
@@ -0,0 +1,180 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008, 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1()
+ {
+ throw 20;
+ }
+
+ // Throw an exception in another function.
+ void function2()
+ {
+ function1();
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 ()
+ {
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow ()
+ {
+ try
+ {
+ function1 ();
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish ()
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 ();
+ }
+ catch (int x)
+ {
+ }
+ function1 (); // marker for until
+ }
+
+ void until ()
+ {
+ function1 ();
+ function1 (); // until here
+ }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main ()
+{
+ try
+ {
+ next_cases.function1 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // This is duplicated so we can next over one but step into
+ // another.
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ next_cases.function3 ();
+
+ try
+ {
+ next_cases.rethrow ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ try
+ {
+ // Another duplicate so we can test "finish".
+ next_cases.function2 ();
+ }
+ catch (...)
+ {
+ // Discard
+ }
+
+ // Another test for "finish".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until".
+ try
+ {
+ next_cases.finish ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "until" with an argument.
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+
+ // Test of "advance".
+ try
+ {
+ next_cases.until ();
+ }
+ catch (...)
+ {
+ }
+}
+
diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
new file mode 100644
index 0000000..04e9c82
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/gdb9593.exp
@@ -0,0 +1,182 @@
+# Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if { [skip_cplus_tests] } { continue }
+
+set testfile "gdb9593"
+set srcfile ${testfile}.cc
+set binfile $objdir/$subdir/$testfile
+
+# Create and source the file that provides information about the compiler
+# used to compile the test case.
+if [get_compiler_info ${binfile} "c++"] {
+ untested gdb9593.exp
+ return -1
+}
+
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
+ untested gdb9593.exp
+ return -1
+}
+
+# Some targets can't do function calls, so don't even bother with this
+# test.
+if [target_info exists gdb,cannot_call_functions] {
+ setup_xfail "*-*-*" 9593
+ fail "This target can not call functions"
+ continue
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+ perror "couldn't run to main"
+ continue
+}
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook"
+ }
+ -re "No symbol .* in current context.\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ pass "check for unwinder hook"
+ set ok 0
+ }
+}
+if {!$ok} {
+ untested gdb9593.exp
+ return -1
+}
+
+# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 1"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next past catch 1"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 2"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next past catch 2"
+
+gdb_test "step" \
+ ".*function1().*" \
+ "step into function2 1"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a throw 3"
+
+gdb_test "next" \
+ ".*next_cases.function3.*" \
+ "next past catch 3"
+
+gdb_test "next" \
+ ".*next_cases.rethrow.*" \
+ "next over a throw 4"
+
+gdb_test "next" \
+ ".*catch (...).*" \
+ "next over a rethrow"
+
+gdb_test "next" \
+ ".*next_cases.function2.*" \
+ "next after a rethrow"
+
+gdb_test "step" \
+ ".*function1().*" \
+ "step into function2 2"
+
+gdb_test "finish" \
+ ".*catch (...).*" \
+ "finish 1"
+
+gdb_test "next" \
+ ".*next_cases.finish ().*" \
+ "next past catch 4"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into finish method"
+
+gdb_test "finish" \
+ ".*catch (...).*" \
+ "finish 2"
+
+gdb_test "next" \
+ ".*next_cases.finish ().*" \
+ "next past catch 5"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into finish, for until"
+
+gdb_test "until" \
+ ".*catch .int x.*" \
+ "until with no argument 1"
+
+set line [gdb_get_line_number "marker for until" $testfile.cc]
+
+gdb_test "until $line" \
+ ".*function1 ().*" \
+ "next past catch 6"
+
+gdb_test "until" \
+ ".*catch (...).*" \
+ "until with no argument 2"
+
+set line [gdb_get_line_number "until here" $testfile.cc]
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 6"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until"
+
+gdb_test "until $line" \
+ ".*catch (...).*" \
+ "until-over-throw"
+
+gdb_test "next" \
+ ".*next_cases.until ().*" \
+ "next past catch 7"
+
+gdb_test "step" \
+ ".*function1 ().*" \
+ "step into until, for advance"
+
+gdb_test "advance $line" \
+ ".*catch (...).*" \
+ "advance-over-throw"
diff --git a/gdb/testsuite/gdb.java/jnpe.exp b/gdb/testsuite/gdb.java/jnpe.exp
new file mode 100644
index 0000000..f16c750
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.exp
@@ -0,0 +1,74 @@
+# Copyright 2009, 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+load_lib "java.exp"
+
+set testfile "jnpe"
+set srcfile ${testfile}.java
+set binfile ${objdir}/${subdir}/${testfile}
+if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
+ untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
+ return -1
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+set line [gdb_get_line_number "break here" $testfile.java]
+gdb_test "break $testfile.java:$line" ""
+
+gdb_test "run" \
+ "// break here.*" \
+ "run java next-over-throw"
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook in java" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook in java"
+ }
+ -re "No symbol .* in current context.?\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ setup_xfail *-*-*
+ fail "check for unwinder hook in java"
+ set ok 0
+ }
+}
+if {!$ok} {
+ untested jnpe.exp
+ return -1
+}
+
+gdb_test "handle SIGSEGV nostop noprint" \
+ "SIGSEGV.*fault" \
+ "disable SIGSEGV for next-over-NPE"
+
+# The line where we stop differ according to gcj; check just we did not already
+# execute the catch point.
+
+gdb_test "next" \
+ "" \
+ "next over NPE"
+
+gdb_breakpoint [gdb_get_line_number "catch point"]
+gdb_continue_to_breakpoint "catch point" ".*// catch point.*"
diff --git a/gdb/testsuite/gdb.java/jnpe.java b/gdb/testsuite/gdb.java/jnpe.java
new file mode 100644
index 0000000..3524830
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.java
@@ -0,0 +1,38 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class jnpe
+{
+ public static String npe ()
+ {
+ return ((Object) null).toString();
+ }
+
+ public static void main (String[] args)
+ {
+ try
+ {
+ System.out.println (npe ()); // break here
+ }
+ catch (NullPointerException n)
+ {
+ System.out.println ("success"); // catch point
+ }
+ }
+}
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-10-07 1:37 Tom Tromey
@ 2010-11-24 17:53 ` Joel Brobecker
2010-11-24 18:24 ` Tom Tromey
2010-11-25 7:59 ` Jan Kratochvil
2010-12-02 15:32 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Joel Brobecker @ 2010-11-24 17:53 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
> I'd appreciate comments. I don't want to commit this without a review,
> as my experience is that I usually err when touching breakpoints or
> infrun.
Outch - no feedback as far as I can tell since the patch as posted
something like 6 weeks ago :-(. We are all so busy these days...
When that happens, I suggest we avoid being too conservative on the HEAD,
especially this far before the next branch creation. If you've already
had Pedro's comments and you think you addressed them, how about going
ahead? We can always revert later on...
> 2010-10-06 Tom Tromey <tromey@redhat.com>
>
> * infrun.c (handle_inferior_event): Handle exception breakpoints.
> (handle_inferior_event): Likewise.
> (insert_exception_resume_breakpoint): New function.
> (check_exception_resume): Likewise.
> * inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
> * infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
> (step_1): Set thread's initiating frame.
> (until_next_continuation): New function.
> (until_next_command): Support exception breakpoints.
> (finish_command_continuation): Delete longjmp breakpoint.
> (finish_forward): Support exception breakpoints.
> * gdbthread.h (struct thread_info) <initiating_frame>: New field.
> * breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
> bp_exception_master>: New constants.
> (struct bpstat_what) <is_longjmp>: New field.
> * breakpoint.c (create_exception_master_breakpoint): New function.
> (update_breakpoints_after_exec): Handle bp_exception_master. Call
> create_exception_master_breakpoint.
> (print_it_typical): Handle bp_exception_master, bp_exception.
> (bpstat_stop_status): Handle bp_exception_master.
> (bpstat_what): Handle bp_exception_master, bp_exception,
> bp_exception_resume.
> (bptype_string): Likewise.
> (print_one_breakpoint_location): Likewise.
> (allocate_bp_location): Likewise.
> (set_longjmp_breakpoint): Handle exception breakpoints.
> (delete_longjmp_breakpoint): Likewise.
> (mention): Likewise.
> (struct until_break_command_continuation_args) <thread_num>: New
> field.
> (until_break_command_continuation): Call
> delete_longjmp_breakpoint.
> (until_break_command): Support exception breakpoints.
> (delete_command): Likewise.
> (breakpoint_re_set_one): Likewise.
> (breakpoint_re_set): Likewise.
>
> 2010-10-06 Tom Tromey <tromey@redhat.com>
>
> * gdb.java/jnpe.java: New file.
> * gdb.java/jnpe.exp: New file.
> * gdb.cp/gdb9593.exp: New file.
> * gdb.cp/gdb9593.cc: New file.
I started reviewing the patch, and quickly realized that this is
flying way over my head, and would require a significant amount
of time for me to really understand...
I did notice a couple of tiny details, though:
> + case bp_exception_master:
> + /* These should never be enabled. */
> + printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
> + result = PRINT_NOTHING;
> + break;
I understand that an assert or internal-error would be unnecessarily
catastrophic for the user, but how about a warning instead? (just a
thought - this is really cosmetic)
> +
> + /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME. True if we are
> + handling a longjmp, false if we are handling an exception. */
> + int is_longjmp;
This component is also used with BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.
Sorry for not being of any real help in this case...
--
Joel
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-11-24 17:53 ` Joel Brobecker
@ 2010-11-24 18:24 ` Tom Tromey
0 siblings, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2010-11-24 18:24 UTC (permalink / raw)
To: Joel Brobecker; +Cc: gdb-patches
>>>>> "Joel" == Joel Brobecker <brobecker@adacore.com> writes:
Joel> We are all so busy these days... When that happens, I suggest we
Joel> avoid being too conservative on the HEAD, especially this far
Joel> before the next branch creation. If you've already had Pedro's
Joel> comments and you think you addressed them, how about going ahead?
Joel> We can always revert later on...
I will consider it. If I do it will not be until next week at the
earliest.
Tom> + case bp_exception_master:
Tom> + /* These should never be enabled. */
Tom> + printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
Tom> + result = PRINT_NOTHING;
Tom> + break;
Joel> I understand that an assert or internal-error would be unnecessarily
Joel> catastrophic for the user, but how about a warning instead? (just a
Joel> thought - this is really cosmetic)
I just copied what the other cases in that function do.
Maybe they should be changed -- I am not sure.
If so, that should be a separate patch.
Tom> + /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME. True if we are
Tom> + handling a longjmp, false if we are handling an exception. */
Tom> + int is_longjmp;
Joel> This component is also used with BPSTAT_WHAT_CLEAR_LONGJMP_RESUME.
Thanks, I updated my branch.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-10-07 1:37 Tom Tromey
2010-11-24 17:53 ` Joel Brobecker
@ 2010-11-25 7:59 ` Jan Kratochvil
2010-11-27 17:25 ` Doug Evans
` (2 more replies)
2010-12-02 15:32 ` Tom Tromey
2 siblings, 3 replies; 40+ messages in thread
From: Jan Kratochvil @ 2010-11-25 7:59 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Thu, 07 Oct 2010 03:37:38 +0200, Tom Tromey wrote:
> * breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
> bp_exception_master>: New constants.
Therefore it is a precent new `bptype's are permitted instead of using
breakpoint_ops (which may need some extensions, not sure).
It may be explained also by the Joel's difficulties to do so:
http://sourceware.org/ml/gdb-patches/2009-06/msg00298.html
BTW the testcase does not work on neither ppc32 nor on ppc64. ppc32 due to
some unexpected next/step stop lines. I see two places in the code using
CORE_ADDR where is probably gdbarch_convert_from_func_ptr_addr appropriate for
the ppc64 case.
> @@ -8524,6 +8588,10 @@ until_break_command (char *arg, int from_tty, int anywhere)
> frame_unwind_caller_id (frame),
> bp_until);
> make_cleanup_delete_breakpoint (breakpoint2);
> +
> + set_longjmp_breakpoint (thread);
> + tp->initiating_frame = frame_unwind_caller_id (frame);
tp->initiating_frame should be initialized from set_longjmp_breakpoint, as it
is required for that operation.
It probably should not be placed in TP. If we are stepping/until-ing/etc.
some code and execute some breakpoint's command list trying to step/next/etc.
again already from a different frame it won't work. But this is a problem for
most of the TP variables already so that's OK for this patch. It should be
carried over from the set-breakpoint to resume-breakpoint otherwise.
> +static void
> +until_next_continuation (void *arg)
> +{
> + struct thread_info *tp = arg;
Missing empty line. :-)
> + delete_longjmp_breakpoint (tp->num);
> +}
> @@ -1270,7 +1284,19 @@ until_next_command (int from_tty)
>
> tp->step_multi = 0; /* Only one call to proceed */
>
> + set_longjmp_breakpoint (thread);
> + tp->initiating_frame = get_frame_id (frame);
> + old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
> +
> proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
> +
> + if (target_can_async_p () && is_running (inferior_ptid))
> + {
> + discard_cleanups (old_chain);
> + add_continuation (tp, until_next_continuation, tp, NULL);
continuation_free_args is NULL here but I think the breakpoint should get
deleted even if there is some premature thread deletion. But maybe just all
the breakpoints specific for that thread (clear_thread_inferior_resources)
should be deleted which would also solve this problem?
> +static void
> +insert_exception_resume_breakpoint (struct thread_info *tp,
> + struct block *b,
> + struct frame_info *frame,
> + struct symbol *sym)
> +{
> + struct gdb_exception e;
> +
> + /* We want to ignore errors here. */
> + TRY_CATCH (e, RETURN_MASK_ALL)
Shouldn't it be RETURN_MASK_ERROR then? Otherwise it should rethrow QUIT (I
hope it works that way) but no such cleanups are probably needed here.
> +static void
> +check_exception_resume (struct execution_control_state *ecs,
> + struct frame_info *frame, struct symbol *func)
> +{
> + struct gdb_exception e;
> +
> + TRY_CATCH (e, RETURN_MASK_ALL)
again RETURN_MASK_ERROR probably.
> diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
> new file mode 100644
> index 0000000..04e9c82
> --- /dev/null
> +++ b/gdb/testsuite/gdb.cp/gdb9593.exp
Please do not use numeric names for testcases. When dealing with them during
regression testing / rebasing etc. it makes more difficult to keep track of
all the numbers and what they were testing.
> +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } {
> + untested gdb9593.exp
> + return -1
> +}
maybe prepare_for_testing?
(nitpick)
> +gdb_exit
> +gdb_start
> +gdb_reinitialize_dir $srcdir/$subdir
> +gdb_load ${binfile}
prepare_for_testing / clean_restart
(nitpick)
> +if {!$ok} {
> + untested gdb9593.exp
rather unsupported? Maybe not, just a hint.
> + return -1
> +}
> +
> +# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
> +
> +gdb_test "next" \
> + ".*catch (...).*" \
> + "next over a throw 1"
These all have redundant leading `.*'. `(...)' is not backslashed.
> --- /dev/null
> +++ b/gdb/testsuite/gdb.java/jnpe.exp
[...]
> +set testfile "jnpe"
> +set srcfile ${testfile}.java
> +set binfile ${objdir}/${subdir}/${testfile}
> +if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
> + untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
> + return -1
> +}
maybe prepare_for_testing?
(nitpick)
> +# The line where we stop differ according to gcj; check just we did not already
> +# execute the catch point.
> +
> +gdb_test "next" \
> + "" \
Here should be ".*" (such sanity checking is missing now in lib/gdb.exp).
> --- /dev/null
> +++ b/gdb/testsuite/gdb.java/jnpe.java
> @@ -0,0 +1,38 @@
> +// Test next-over-NPE.
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2009 Free Software Foundation, Inc.
+2010
> + public static void main (String[] args)
> + {
> + try
> + {
> + System.out.println (npe ()); // break here
> + }
> + catch (NullPointerException n)
> + {
> + System.out.println ("success"); // catch point
BTW the testcase fives on Fedora 14 x86_64 and i686:
+FAIL: gdb.java/jnpe.exp: continue to breakpoint: catch point
but this is due to -O0 -g .debug_line wrong in this case.
Breakpoint for line 35 is at 0x400cb3:
0x0000000000400cb3 <+112>: mov $0x601560,%edi
0x0000000000400cb8 <+117>: callq 0x400a18 <_Jv_InitClass@plt>
0x0000000000400cbd <+122>: mov $0x1,%ebx
But the execution goes:
(gdb) stepi
0x0000000000400d0d 33 catch (NullPointerException n)
2: x/i $pc
=> 0x400d0d <jnpe.main(java.lang.String[])void+202>: mov (%rax),%rax
(gdb) stepi
35 System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400d10 <jnpe.main(java.lang.String[])void+205>: test %bl,%bl
(gdb) stepi
0x0000000000400d12 35 System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400d12 <jnpe.main(java.lang.String[])void+207>:
jne 0x400cbd <jnpe.main(java.lang.String[])void+122>
(gdb) stepi
0x0000000000400cbd 35 System.out.println ("success"); // catch point
2: x/i $pc
=> 0x400cbd <jnpe.main(java.lang.String[])void+122>: mov $0x1,%ebx
> + }
> + }
> +}
Thanks,
Jan
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-11-25 7:59 ` Jan Kratochvil
@ 2010-11-27 17:25 ` Doug Evans
2010-11-28 8:29 ` Joel Brobecker
2010-11-30 16:43 ` Tom Tromey
2010-11-30 18:23 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Doug Evans @ 2010-11-27 17:25 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: Tom Tromey, gdb-patches
On Wed, Nov 24, 2010 at 11:58 PM, Jan Kratochvil
<jan.kratochvil@redhat.com> wrote:
>> diff --git a/gdb/testsuite/gdb.cp/gdb9593.exp b/gdb/testsuite/gdb.cp/gdb9593.exp
>> new file mode 100644
>> index 0000000..04e9c82
>> --- /dev/null
>> +++ b/gdb/testsuite/gdb.cp/gdb9593.exp
>
> Please do not use numeric names for testcases. When dealing with them during
> regression testing / rebasing etc. it makes more difficult to keep track of
> all the numbers and what they were testing.
For reference sake,
if this is to become a rule, I think we should codify it in the coding
standards.
Personally, I don't mind the numbers, but I'm happy to go with the flow.
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-27 17:25 ` Doug Evans
@ 2010-11-28 8:29 ` Joel Brobecker
0 siblings, 0 replies; 40+ messages in thread
From: Joel Brobecker @ 2010-11-28 8:29 UTC (permalink / raw)
To: Doug Evans; +Cc: Jan Kratochvil, Tom Tromey, gdb-patches
> For reference sake,
> if this is to become a rule, I think we should codify it in the coding
> standards.
>
> Personally, I don't mind the numbers, but I'm happy to go with the flow.
My two cents:
To me, the numbers mean nothing. They are useful to get more background
info on the original problem, but a comment in the script would also
work. On the other hand, when searching testcases that test a certain
feature/scenario, more descriptive names often help.
--
Joel
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-25 7:59 ` Jan Kratochvil
2010-11-27 17:25 ` Doug Evans
@ 2010-11-30 16:43 ` Tom Tromey
2010-11-30 17:02 ` Jan Kratochvil
` (2 more replies)
2010-11-30 18:23 ` Tom Tromey
2 siblings, 3 replies; 40+ messages in thread
From: Tom Tromey @ 2010-11-30 16:43 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
Thanks for the review.
I've made most of the changes you recommended.
>> * breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
>> bp_exception_master> : New constants.
Jan> Therefore it is a precent new `bptype's are permitted instead of using
Jan> breakpoint_ops (which may need some extensions, not sure).
Yeah. Mostly I just copied and then extended the existing longjmp
support.
I looked briefly at using breakpoint_ops, but it seems like it would
require a bunch of new methods that are specific to just this
breakpoint. Maybe that is the way to go?
Jan> BTW the testcase does not work on neither ppc32 nor on ppc64.
Thanks, I will investigate.
Jan> tp-> initiating_frame should be initialized from
Jan> set_longjmp_breakpoint, as it is required for that operation.
I made this change.
Jan> It probably should not be placed in TP. If we are
Jan> stepping/until-ing/etc. some code and execute some breakpoint's
Jan> command list trying to step/next/etc. again already from a
Jan> different frame it won't work. But this is a problem for most of
Jan> the TP variables already so that's OK for this patch. It should be
Jan> carried over from the set-breakpoint to resume-breakpoint
Jan> otherwise.
I thought that gdb did not support nested inferior-control commands like
this.
It would be a nice feature.
>> + add_continuation (tp, until_next_continuation, tp, NULL);
Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?
I will try to make a test case so I can see what actually happens in
this scenario. Thanks for pointing it out.
>> +if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
>> + untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
>> + return -1
Jan> maybe prepare_for_testing?
Jan> (nitpick)
I left this as-is. compile_java_from_source does some extra processing
right now.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-11-30 16:43 ` Tom Tromey
@ 2010-11-30 17:02 ` Jan Kratochvil
2010-11-30 17:15 ` Phil Muldoon
2010-11-30 20:15 ` Tom Tromey
2010-12-01 21:40 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Jan Kratochvil @ 2010-11-30 17:02 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches, Phil Muldoon
On Tue, 30 Nov 2010 17:43:28 +0100, Tom Tromey wrote:
> I looked briefly at using breakpoint_ops, but it seems like it would
> require a bunch of new methods that are specific to just this
> breakpoint. Maybe that is the way to go?
One day probably.
> Jan> It probably should not be placed in TP. If we are
> Jan> stepping/until-ing/etc. some code and execute some breakpoint's
> Jan> command list trying to step/next/etc. again already from a
> Jan> different frame it won't work. But this is a problem for most of
> Jan> the TP variables already so that's OK for this patch. It should be
> Jan> carried over from the set-breakpoint to resume-breakpoint
> Jan> otherwise.
>
> I thought that gdb did not support nested inferior-control commands like
> this.
>
> It would be a nice feature.
I was thinking Phil was coding something like that with Python commands
attached to breakpoints. It would need more fixes but I was just pointing
out an incompatibility also in this code.
With current GDB it seems OK to me.
> >> +if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
> >> + untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
> >> + return -1
>
> Jan> maybe prepare_for_testing?
> Jan> (nitpick)
>
> I left this as-is. compile_java_from_source does some extra processing
> right now.
I see now, sorry.
Thanks,
Jan
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-11-30 17:02 ` Jan Kratochvil
@ 2010-11-30 17:15 ` Phil Muldoon
0 siblings, 0 replies; 40+ messages in thread
From: Phil Muldoon @ 2010-11-30 17:15 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: Tom Tromey, gdb-patches
Jan Kratochvil <jan.kratochvil@redhat.com> writes:
>> I thought that gdb did not support nested inferior-control commands like
>> this.
>>
>> It would be a nice feature.
>
> I was thinking Phil was coding something like that with Python commands
> attached to breakpoints. It would need more fixes but I was just pointing
> out an incompatibility also in this code.
>
> With current GDB it seems OK to me.
Conditional breakpoints in Python that evaluate a Python function to
determine whether or not to stop. Though there is no reason why in the
future what you describe could not happen with commands attached to a
breakpoint.
Cheers,
Phil
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-30 16:43 ` Tom Tromey
2010-11-30 17:02 ` Jan Kratochvil
@ 2010-11-30 20:15 ` Tom Tromey
2010-12-01 13:42 ` Jan Kratochvil
2010-12-01 21:40 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2010-11-30 20:15 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
>>> + add_continuation (tp, until_next_continuation, tp, NULL);
Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?
Tom> I will try to make a test case so I can see what actually happens in
Tom> this scenario. Thanks for pointing it out.
Good catch here, we were leaving around the exception breakpoints.
Calling delete_longjmp_breakpoint from clear_thread_inferior_resources
worked for me. I'm not 100% confident that this is the correct fix
though.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-30 20:15 ` Tom Tromey
@ 2010-12-01 13:42 ` Jan Kratochvil
0 siblings, 0 replies; 40+ messages in thread
From: Jan Kratochvil @ 2010-12-01 13:42 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Tue, 30 Nov 2010 21:15:26 +0100, Tom Tromey wrote:
> Jan> But maybe just all the breakpoints specific for that thread
> Jan> (clear_thread_inferior_resources) should be deleted which would
> Jan> also solve this problem?
Found out now I already filed it as:
delete thread-specific breakpoint on the thread exit
http://sourceware.org/bugzilla/show_bug.cgi?id=11568
> Calling delete_longjmp_breakpoint from clear_thread_inferior_resources
> worked for me. I'm not 100% confident that this is the correct fix though.
It is also not much a serious problem.
Thanks,
Jan
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-30 16:43 ` Tom Tromey
2010-11-30 17:02 ` Jan Kratochvil
2010-11-30 20:15 ` Tom Tromey
@ 2010-12-01 21:40 ` Tom Tromey
2 siblings, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2010-12-01 21:40 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
Jan> BTW the testcase does not work on neither ppc32 nor on ppc64.
Tom> Thanks, I will investigate.
On PPC32 the problem was just that the test case was not robust against
differences between compilers on different arches. In particular,
different versions of gcc seem to give different (sometimes even quite
weird) line numbers to the landing point of an exception.
I fixed this by rewriting the test case to be more robust against these
kinds of differences.
On PPC64, as you surmised, the problem was related to function
descriptors. In particular, create_exception_master_breakpoint
was calling lookup_minimal_symbol_text -- but that failed on PPC64.
FWIW I expect that the longjmp code also has a problem here, but again
probably masked by glibc PC mangling.
I think I've addressed all the comments. I'll send an updated patch
tomorrow.
Tom
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-11-25 7:59 ` Jan Kratochvil
2010-11-27 17:25 ` Doug Evans
2010-11-30 16:43 ` Tom Tromey
@ 2010-11-30 18:23 ` Tom Tromey
2010-11-30 18:55 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2010-11-30 18:23 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
Jan> continuation_free_args is NULL here but I think the breakpoint
Jan> should get deleted even if there is some premature thread deletion.
Jan> But maybe just all the breakpoints specific for that thread
Jan> (clear_thread_inferior_resources) should be deleted which would
Jan> also solve this problem?
It turns out there is a bug here in CVS gdb, with longjmp breakpoints.
Consider the appended program. Set a breakpoint in thr_func, run, then
"next". I get:
(gdb) n
Warning:
Cannot insert breakpoint 0.
Error accessing memory address 0x83f4375: Input/output error.
0x00757b01 in __libc_siglongjmp (env=0xb7fd8444, val=1) at longjmp.c:30
30 {
I'm looking into it some more.
Tom
#include <pthread.h>
void throwit () {
// throw 1;
pthread_exit ((void*) "lo");
}
void *
thr_func (void *)
{
try {
throwit ();
}
catch (int x) {
return (void*) "hi bob";
}
return NULL;
}
int main ()
{
pthread_t thread;
void *r;
pthread_create (&thread, NULL, thr_func, NULL);
pthread_join (thread, &r);
}
^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: RFC: next/finish/etc -vs- exceptions
2010-10-07 1:37 Tom Tromey
2010-11-24 17:53 ` Joel Brobecker
2010-11-25 7:59 ` Jan Kratochvil
@ 2010-12-02 15:32 ` Tom Tromey
2010-12-09 16:37 ` Tom Tromey
2 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2010-12-02 15:32 UTC (permalink / raw)
To: gdb-patches
Here is an updated version of the next-over-throw patch.
I think I've addressed all of Jan's comments.
Built and regtested on x86-64 (compile farm).
I also did various tests on PPC32 and PPC64 machines.
Let me know what you think.
In the absence of comments I will check this in next week.
Tom
2010-12-02 Tom Tromey <tromey@redhat.com>
PR c++/9593:
* thread.c (clear_thread_inferior_resources): Call
delete_longjmp_breakpoint.
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(handle_inferior_event): Likewise.
(insert_exception_resume_breakpoint): New function.
(check_exception_resume): Likewise.
* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
(step_1): Set thread's initiating frame.
(until_next_continuation): New function.
(until_next_command): Support exception breakpoints.
(finish_command_continuation): Delete longjmp breakpoint.
(finish_forward): Support exception breakpoints.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
bp_exception_master>: New constants.
(struct bpstat_what) <is_longjmp>: New field.
(set_longjmp_breakpoint): Update.
* breakpoint.c (create_exception_master_breakpoint): New function.
(update_breakpoints_after_exec): Handle bp_exception_master. Call
create_exception_master_breakpoint.
(print_it_typical): Handle bp_exception_master, bp_exception.
(bpstat_stop_status): Handle bp_exception_master.
(bpstat_what): Handle bp_exception_master, bp_exception,
bp_exception_resume.
(bptype_string): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(set_longjmp_breakpoint): Handle exception breakpoints. Change
interface.
(delete_longjmp_breakpoint): Handle exception breakpoints.
(mention): Likewise.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Support exception breakpoints.
(delete_command): Likewise.
(breakpoint_re_set_one): Likewise.
(breakpoint_re_set): Likewise.
2010-12-02 Tom Tromey <tromey@redhat.com>
* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* gdb.cp/nextoverthrow.exp: New file.
* gdb.cp/nextoverthrow.cc: New file.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 4affe0a..11bfc11 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2201,6 +2201,35 @@ create_std_terminate_master_breakpoint (const char *func_name)
do_cleanups (old_chain);
}
+/* Install a master breakpoint on the unwinder's debug hook. */
+
+void
+create_exception_master_breakpoint (void)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *debug_hook;
+
+ debug_hook = lookup_minimal_symbol ("_Unwind_DebugHook", NULL, objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+ CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (debug_hook);
+ struct gdbarch *gdbarch = get_objfile_arch (objfile);
+
+ addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+ ¤t_target);
+ b = create_internal_breakpoint (gdbarch, addr, bp_exception_master);
+ b->addr_string = xstrdup ("_Unwind_DebugHook");
+ b->enable_state = bp_disabled;
+ }
+ }
+
+ update_global_location_list (1);
+}
+
void
update_breakpoints_after_exec (void)
{
@@ -2242,7 +2271,8 @@ update_breakpoints_after_exec (void)
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master || b->type == bp_std_terminate_master)
+ || b->type == bp_longjmp_master || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
@@ -2257,7 +2287,8 @@ update_breakpoints_after_exec (void)
/* Longjmp and longjmp-resume breakpoints are also meaningless
after an exec. */
- if (b->type == bp_longjmp || b->type == bp_longjmp_resume)
+ if (b->type == bp_longjmp || b->type == bp_longjmp_resume
+ || b->type == bp_exception || b->type == bp_exception_resume)
{
delete_breakpoint (b);
continue;
@@ -2319,6 +2350,7 @@ update_breakpoints_after_exec (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
int
@@ -3237,6 +3269,12 @@ print_it_typical (bpstat bs)
result = PRINT_NOTHING;
break;
+ case bp_exception_master:
+ /* These should never be enabled. */
+ printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
+ result = PRINT_NOTHING;
+ break;
+
case bp_watchpoint:
case bp_hardware_watchpoint:
annotate_watchpoint (b->number);
@@ -3324,6 +3362,8 @@ print_it_typical (bpstat bs)
case bp_none:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4075,7 +4115,8 @@ bpstat_stop_status (struct address_space *aspace,
if (b->type == bp_thread_event || b->type == bp_overlay_event
|| b->type == bp_longjmp_master
- || b->type == bp_std_terminate_master)
+ || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
@@ -4187,6 +4228,7 @@ bpstat_what (bpstat bs)
retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;
for (; bs != NULL; bs = bs->next)
{
@@ -4242,10 +4284,15 @@ bpstat_what (bpstat bs)
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bs->breakpoint_at->owner->type == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp
+ = bs->breakpoint_at->owner->type == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -4261,6 +4308,7 @@ bpstat_what (bpstat bs)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
this_action = BPSTAT_WHAT_SINGLE;
break;
case bp_catchpoint:
@@ -4461,6 +4509,8 @@ print_one_breakpoint_location (struct breakpoint *b,
{bp_access_watchpoint, "acc watchpoint"},
{bp_longjmp, "longjmp"},
{bp_longjmp_resume, "longjmp resume"},
+ {bp_exception, "exception"},
+ {bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
@@ -4470,6 +4520,7 @@ print_one_breakpoint_location (struct breakpoint *b,
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
{bp_std_terminate_master, "std::terminate master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
{bp_fast_tracepoint, "fast tracepoint"},
@@ -4595,6 +4646,8 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4604,6 +4657,7 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
case bp_tracepoint:
case bp_fast_tracepoint:
case bp_static_tracepoint:
@@ -5334,6 +5388,8 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -5344,6 +5400,7 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -5548,13 +5605,14 @@ make_breakpoint_permanent (struct breakpoint *b)
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in TP. FRAME is the frame which
+ initiated the operation. */
void
-set_longjmp_breakpoint (int thread)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
{
struct breakpoint *b, *temp;
+ int thread = tp->num;
/* To avoid having to rescan all objfile symbols at every step,
we maintain a list of continually-inserted but always disabled
@@ -5562,13 +5620,16 @@ set_longjmp_breakpoint (int thread)
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
if (b->pspace == current_program_space
- && b->type == bp_longjmp_master)
+ && (b->type == bp_longjmp_master
+ || b->type == bp_exception_master))
{
struct breakpoint *clone = clone_momentary_breakpoint (b);
- clone->type = bp_longjmp;
+ clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
clone->thread = thread;
}
+
+ tp->initiating_frame = frame;
}
/* Delete all longjmp breakpoints from THREAD. */
@@ -5578,7 +5639,7 @@ delete_longjmp_breakpoint (int thread)
struct breakpoint *b, *temp;
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp)
+ if (b->type == bp_longjmp || b->type == bp_exception)
{
if (b->thread == thread)
delete_breakpoint (b);
@@ -6750,6 +6811,8 @@ mention (struct breakpoint *b)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
@@ -6760,6 +6823,7 @@ mention (struct breakpoint *b)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}
@@ -8346,6 +8410,7 @@ struct until_break_command_continuation_args
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};
/* This function is called by fetch_inferior_event via the
@@ -8360,6 +8425,7 @@ until_break_command_continuation (void *arg)
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}
void
@@ -8371,6 +8437,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;
clear_proceed_status ();
@@ -8409,6 +8477,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ tp = inferior_thread ();
+ thread = tp->num;
+
/* Keep within the current frame, or in frames called by the current
one. */
@@ -8421,6 +8492,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8437,6 +8511,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
@@ -9658,6 +9733,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
@@ -9679,6 +9755,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
@@ -10139,6 +10216,7 @@ breakpoint_re_set_one (void *bint)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
@@ -10162,6 +10240,8 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_jit_event:
break;
}
@@ -10205,6 +10285,7 @@ breakpoint_re_set (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
\f
/* Reset the thread number of this breakpoint:
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 6f5d050..6eadb34 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -56,6 +56,13 @@ enum bptype
bp_longjmp, /* secret breakpoint to find longjmp() */
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
+ /* An internal breakpoint that is installed on the unwinder's
+ debug hook. */
+ bp_exception,
+ /* An internal breakpoint that is set at the point where an
+ exception will land. */
+ bp_exception_resume,
+
/* Used by wait_for_inferior for stepping over subroutine calls, for
stepping over signal handlers, and for skipping prologues. */
bp_step_resume,
@@ -125,6 +132,9 @@ enum bptype
/* Master copies of std::terminate breakpoints. */
bp_std_terminate_master,
+ /* Like bp_longjmp_master, but for exceptions. */
+ bp_exception_master,
+
bp_catchpoint,
bp_tracepoint,
@@ -653,6 +663,11 @@ struct bpstat_what
continuing from a call dummy without popping the frame is not a
useful one). */
enum stop_stack_kind call_dummy;
+
+ /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME and
+ BPSTAT_WHAT_CLEAR_LONGJMP_RESUME. True if we are handling a
+ longjmp, false if we are handling an exception. */
+ int is_longjmp;
};
/* The possible return values for print_bpstat, print_it_normal,
@@ -887,7 +902,8 @@ extern int detach_breakpoints (int);
this PSPACE anymore. */
extern void breakpoint_program_space_exit (struct program_space *pspace);
-extern void set_longjmp_breakpoint (int thread);
+extern void set_longjmp_breakpoint (struct thread_info *tp,
+ struct frame_id frame);
extern void delete_longjmp_breakpoint (int thread);
extern void enable_overlay_breakpoints (void);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index cd24eaf..9638368 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -66,6 +66,9 @@ struct thread_info
/* Step-resume or longjmp-resume breakpoint. */
struct breakpoint *step_resume_breakpoint;
+ /* Exception-resume breakpoint. */
+ struct breakpoint *exception_resume_breakpoint;
+
/* Range to single step within.
If this is nonzero, respond to a single-step signal by continuing
@@ -185,6 +188,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
@@ -221,6 +228,9 @@ extern void delete_thread_silent (ptid_t);
/* Delete a step_resume_breakpoint from the thread database. */
extern void delete_step_resume_breakpoint (struct thread_info *);
+/* Delete an exception_resume_breakpoint from the thread database. */
+extern void delete_exception_resume_breakpoint (struct thread_info *);
+
/* Translate the integer thread id (GDB's homegrown id, not the system's)
into a "pid" (which may be overloaded with extra thread information). */
extern ptid_t thread_id_to_pid (int);
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c4cdb06..ca7d094 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -822,7 +822,7 @@ nexti_command (char *count_string, int from_tty)
step_1 (1, 1, count_string);
}
-static void
+void
delete_longjmp_breakpoint_cleanup (void *arg)
{
int thread = * (int *) arg;
@@ -862,10 +862,12 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
if (!single_inst || skip_subroutines) /* leave si command alone */
{
+ struct thread_info *tp = inferior_thread ();
+
if (in_thread_list (inferior_ptid))
thread = pid_to_thread_id (inferior_ptid);
- set_longjmp_breakpoint (thread);
+ set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1219,6 +1221,16 @@ signal_command (char *signum_exp, int from_tty)
proceed ((CORE_ADDR) -1, oursig, 0);
}
+/* A continuation callback for until_next_command. */
+
+static void
+until_next_continuation (void *arg)
+{
+ struct thread_info *tp = arg;
+
+ delete_longjmp_breakpoint (tp->num);
+}
+
/* Proceed until we reach a different source line with pc greater than
our current one or exit the function. We skip calls in both cases.
@@ -1235,6 +1247,8 @@ until_next_command (int from_tty)
struct symbol *func;
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
+ int thread = tp->num;
+ struct cleanup *old_chain;
clear_proceed_status ();
set_step_frame ();
@@ -1270,7 +1284,18 @@ until_next_command (int from_tty)
tp->step_multi = 0; /* Only one call to proceed */
+ set_longjmp_breakpoint (tp, get_frame_id (frame));
+ old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+ if (target_can_async_p () && is_running (inferior_ptid))
+ {
+ discard_cleanups (old_chain);
+ add_continuation (tp, until_next_continuation, tp, NULL);
+ }
+ else
+ do_cleanups (old_chain);
}
static void
@@ -1463,6 +1488,7 @@ finish_command_continuation (void *arg)
if (bs != NULL && tp->proceed_to_finish)
observer_notify_normal_stop (bs, 1 /* print frame */);
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}
static void
@@ -1546,6 +1572,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
struct breakpoint *breakpoint;
struct cleanup *old_chain;
struct finish_command_continuation_args *cargs;
+ int thread = tp->num;
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
@@ -1556,6 +1583,9 @@ finish_forward (struct symbol *function, struct frame_info *frame)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ set_longjmp_breakpoint (tp, get_frame_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
tp->proceed_to_finish = 1; /* We want stop_registers, please... */
cargs = xmalloc (sizeof (*cargs));
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 5abec68..e309277 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -291,6 +291,8 @@ extern void interrupt_target_command (char *args, int from_tty);
extern void interrupt_target_1 (int all_threads);
+extern void delete_longjmp_breakpoint_cleanup (void *arg);
+
extern void detach_command (char *, int);
extern void notice_new_inferior (ptid_t, int, int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 57f2ab8..f6464d4 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -45,6 +45,8 @@
#include "language.h"
#include "solib.h"
#include "main.h"
+#include "dictionary.h"
+#include "block.h"
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
@@ -367,6 +369,7 @@ follow_fork (void)
parent thread structure's run control related fields, not just these.
Initialized to avoid "may be used uninitialized" warnings from gcc. */
struct breakpoint *step_resume_breakpoint = NULL;
+ struct breakpoint *exception_resume_breakpoint = NULL;
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
struct frame_id step_frame_id = { 0 };
@@ -419,6 +422,8 @@ follow_fork (void)
step_range_start = tp->step_range_start;
step_range_end = tp->step_range_end;
step_frame_id = tp->step_frame_id;
+ exception_resume_breakpoint
+ = clone_momentary_breakpoint (tp->exception_resume_breakpoint);
/* For now, delete the parent's sr breakpoint, otherwise,
parent/child sr breakpoints are considered duplicates,
@@ -429,6 +434,7 @@ follow_fork (void)
tp->step_range_start = 0;
tp->step_range_end = 0;
tp->step_frame_id = null_frame_id;
+ delete_exception_resume_breakpoint (tp);
}
parent = inferior_ptid;
@@ -470,6 +476,8 @@ follow_fork (void)
tp->step_range_start = step_range_start;
tp->step_range_end = step_range_end;
tp->step_frame_id = step_frame_id;
+ tp->exception_resume_breakpoint
+ = exception_resume_breakpoint;
}
else
{
@@ -523,6 +531,9 @@ follow_inferior_reset_breakpoints (void)
if (tp->step_resume_breakpoint)
breakpoint_re_set_thread (tp->step_resume_breakpoint);
+ if (tp->exception_resume_breakpoint)
+ breakpoint_re_set_thread (tp->exception_resume_breakpoint);
+
/* Reinsert all breakpoints in the child. The user may have set
breakpoints after catching the fork, in which case those
were never set in the child, but only in the parent. This makes
@@ -760,6 +771,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
/* If there was one, it's gone now. We cannot truly step-to-next
statement through an exec(). */
th->step_resume_breakpoint = NULL;
+ th->exception_resume_breakpoint = NULL;
th->step_range_start = 0;
th->step_range_end = 0;
@@ -2190,6 +2202,8 @@ static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void check_exception_resume (struct execution_control_state *,
+ struct frame_info *, struct symbol *);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
@@ -2313,6 +2327,7 @@ delete_step_resume_breakpoint_callback (struct thread_info *info, void *data)
return 0;
delete_step_resume_breakpoint (info);
+ delete_exception_resume_breakpoint (info);
return 0;
}
@@ -2337,6 +2352,7 @@ delete_step_thread_step_resume_breakpoint (void)
struct thread_info *tp = inferior_thread ();
delete_step_resume_breakpoint (tp);
+ delete_exception_resume_breakpoint (tp);
}
else
/* In all-stop mode, delete all step-resume and longjmp-resume
@@ -4076,23 +4092,33 @@ process_event_stop_test:
ecs->event_thread->stepping_over_breakpoint = 1;
- if (!gdbarch_get_longjmp_target_p (gdbarch)
- || !gdbarch_get_longjmp_target (gdbarch, frame, &jmp_buf_pc))
+ if (what.is_longjmp)
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
+ if (!gdbarch_get_longjmp_target_p (gdbarch)
+ || !gdbarch_get_longjmp_target (gdbarch,
+ frame, &jmp_buf_pc))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "\
infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
- keep_going (ecs);
- return;
- }
+ keep_going (ecs);
+ return;
+ }
- /* We're going to replace the current step-resume breakpoint
- with a longjmp-resume breakpoint. */
- delete_step_resume_breakpoint (ecs->event_thread);
+ /* We're going to replace the current step-resume breakpoint
+ with a longjmp-resume breakpoint. */
+ delete_step_resume_breakpoint (ecs->event_thread);
- /* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ /* Insert a breakpoint at resume address. */
+ insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ }
+ else
+ {
+ struct symbol *func = get_frame_function (frame);
+ if (func)
+ check_exception_resume (ecs, frame, func);
+ }
keep_going (ecs);
return;
@@ -4101,8 +4127,52 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
fprintf_unfiltered (gdb_stdlog,
"infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
- gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
- delete_step_resume_breakpoint (ecs->event_thread);
+ if (what.is_longjmp)
+ {
+ gdb_assert (ecs->event_thread->step_resume_breakpoint != NULL);
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }
+ else
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame. We stop, because the exception has been
+ caught.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+
+ gdb_assert (ecs->event_thread->exception_resume_breakpoint != NULL);
+ delete_exception_resume_breakpoint (ecs->event_thread);
+
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ /* Case 2. Fall through. */
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+
+ /* For Cases 1 and 2, remove the step-resume breakpoint,
+ if it exists. */
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }
ecs->event_thread->stop_step = 1;
print_stop_reason (END_STEPPING_RANGE, 0);
@@ -5070,6 +5140,97 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
+/* Insert an exception resume breakpoint. TP is the thread throwing
+ the exception. The block B is the block of the unwinder debug hook
+ function. FRAME is the frame corresponding to the call to this
+ function. SYM is the symbol of the function argument holding the
+ target PC of the exception. */
+
+static void
+insert_exception_resume_breakpoint (struct thread_info *tp,
+ struct block *b,
+ struct frame_info *frame,
+ struct symbol *sym)
+{
+ struct gdb_exception e;
+
+ /* We want to ignore errors here. */
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ struct symbol *vsym;
+ struct value *value;
+ CORE_ADDR handler;
+ struct breakpoint *bp;
+
+ vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+ value = read_var_value (vsym, frame);
+ /* If the value was optimized out, revert to the old behavior. */
+ if (! value_optimized_out (value))
+ {
+ handler = value_as_address (value);
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: exception resume at %lx\n",
+ (unsigned long) handler);
+
+ bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
+ handler, bp_exception_resume);
+ bp->thread = tp->num;
+ inferior_thread ()->exception_resume_breakpoint = bp;
+ }
+ }
+}
+
+/* This is called when an exception has been intercepted. Check to
+ see whether the exception's destination is of interest, and if so,
+ set an exception resume breakpoint there. */
+
+static void
+check_exception_resume (struct execution_control_state *ecs,
+ struct frame_info *frame, struct symbol *func)
+{
+ struct gdb_exception e;
+
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ struct block *b;
+ struct dict_iterator iter;
+ struct symbol *sym;
+ int argno = 0;
+
+ /* The exception breakpoint is a thread-specific breakpoint on
+ the unwinder's debug hook, declared as:
+
+ void _Unwind_DebugHook (void *cfa, void *handler);
+
+ The CFA argument indicates the frame to which control is
+ about to be transferred. HANDLER is the destination PC.
+
+ We ignore the CFA and set a temporary breakpoint at HANDLER.
+ This is not extremely efficient but it avoids issues in gdb
+ with computing the DWARF CFA, and it also works even in weird
+ cases such as throwing an exception from inside a signal
+ handler. */
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ ALL_BLOCK_SYMBOLS (b, iter, sym)
+ {
+ if (!SYMBOL_IS_ARGUMENT (sym))
+ continue;
+
+ if (argno == 0)
+ ++argno;
+ else
+ {
+ insert_exception_resume_breakpoint (ecs->event_thread,
+ b, frame, sym);
+ break;
+ }
+ }
+ }
+}
+
static void
stop_stepping (struct execution_control_state *ecs)
{
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.cc b/gdb/testsuite/gdb.cp/nextoverthrow.cc
new file mode 100644
index 0000000..b25cb34
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -0,0 +1,203 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <iostream>
+
+using namespace std;
+
+void dummy ()
+{
+}
+
+class NextOverThrowDerivates
+{
+
+public:
+
+
+ // Single throw an exception in this function.
+ void function1 (int val)
+ {
+ throw val;
+ }
+
+ // Throw an exception in another function.
+ void function2 (int val)
+ {
+ function1 (val);
+ }
+
+ // Throw an exception in another function, but handle it
+ // locally.
+ void function3 (int val)
+ {
+ {
+ try
+ {
+ function1 (val);
+ }
+ catch (...)
+ {
+ cout << "Caught and handled function1 exception" << endl;
+ }
+ }
+ }
+
+ void rethrow (int val)
+ {
+ try
+ {
+ function1 (val);
+ }
+ catch (...)
+ {
+ throw;
+ }
+ }
+
+ void finish (int val)
+ {
+ // We use this to test that a "finish" here does not end up in
+ // this frame, but in the one above.
+ try
+ {
+ function1 (val);
+ }
+ catch (int x)
+ {
+ }
+ function1 (val); // marker for until
+ }
+
+ void until (int val)
+ {
+ function1 (val);
+ function1 (val); // until here
+ }
+
+};
+NextOverThrowDerivates next_cases;
+
+
+int main ()
+{
+ int testval = -1;
+
+ try
+ {
+ next_cases.function1 (0); // Start: first test
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first test
+ }
+
+ try
+ {
+ next_cases.function2 (1); // Start: nested throw
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: nested throw
+ }
+
+ try
+ {
+ // This is duplicated so we can next over one but step into
+ // another.
+ next_cases.function2 (2); // Start: step in test
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: step in test
+ }
+
+ next_cases.function3 (3); // Start: next past catch
+ dummy ();
+ testval = 3; // End: next past catch
+
+ try
+ {
+ next_cases.rethrow (4); // Start: rethrow
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: rethrow
+ }
+
+ try
+ {
+ // Another duplicate so we can test "finish".
+ next_cases.function2 (5); // Start: first finish
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first finish
+ }
+
+ // Another test for "finish".
+ try
+ {
+ next_cases.finish (6); // Start: second finish
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: second finish
+ }
+
+ // Test of "until".
+ try
+ {
+ next_cases.finish (7); // Start: first until
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: first until
+ }
+
+ // Test of "until" with an argument.
+ try
+ {
+ next_cases.until (8); // Start: second until
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: second until
+ }
+
+ // Test of "advance".
+ try
+ {
+ next_cases.until (9); // Start: advance
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: advance
+ }
+
+ testval = 32; // done
+}
+
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
new file mode 100644
index 0000000..960ea0d
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -0,0 +1,153 @@
+# Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+if { [skip_cplus_tests] } { continue }
+
+set testfile "nextoverthrow"
+set srcfile ${testfile}.cc
+set binfile $objdir/$subdir/$testfile
+
+# Create and source the file that provides information about the compiler
+# used to compile the test case.
+if [get_compiler_info ${binfile} "c++"] {
+ untested nextoverthrow.exp
+ return -1
+}
+
+if {[prepare_for_testing $testfile.exp $testfile $srcfile {debug c++}]} {
+ return -1
+}
+
+if ![runto_main] then {
+ perror "couldn't run to main"
+ continue
+}
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook"
+ }
+ -re "No symbol .* in current context.\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ pass "check for unwinder hook"
+ set ok 0
+ }
+}
+if {!$ok} {
+ unsupported "nextoverthrow.exp could not find _Unwind_DebugHook"
+ return -1
+}
+
+# Set a temporary breakpoint and then continue to it.
+# The breakpoint is set according to a marker in the file.
+proc tbreak_and_cont {text} {
+ global testfile
+
+ set line [gdb_get_line_number $text $testfile.cc]
+ gdb_breakpoint "$testfile.cc:$line" temporary
+ gdb_test "continue" "Temporary breakpoint.*" "continuing to $text"
+}
+
+# Verify the value of testval.
+proc verify_testval {name value} {
+ gdb_test "print testval == $value" " = true" $name
+}
+
+# See http://sourceware.org/bugzilla/show_bug.cgi?id=9593
+# Our general approach here is to do some operation, verify
+# that testval has not changed, continue to the location at
+# which the next test starts, and verify testval again.
+# This works around platform differences in debuginfo that
+# make looking at the source line unreliable.
+
+# A simple test of next over a throw.
+tbreak_and_cont "Start: first test"
+gdb_test "next" ".*" "next over a throw 1"
+tbreak_and_cont "End: first test"
+verify_testval "pre-check - next over a throw 1" -1
+
+tbreak_and_cont "Start: nested throw"
+verify_testval "post-check - next over a throw 1" 0
+gdb_test "next" ".*" "next over a throw 2"
+tbreak_and_cont "End: nested throw"
+verify_testval "pre-check - next over a throw 2" 0
+
+tbreak_and_cont "Start: step in test"
+verify_testval "post-check - next over a throw 2" 1
+gdb_test "step" "function1().*" "step into function2 1"
+gdb_test "next" ".*" "next over a throw 3"
+tbreak_and_cont "End: step in test"
+verify_testval "pre-check - next over a throw 3" 1
+
+tbreak_and_cont "Start: next past catch"
+verify_testval "post-check - next over a throw 3" 2
+gdb_test "next" ".*" "next past catch"
+tbreak_and_cont "End: next past catch"
+verify_testval "pre-check - next past catch" 2
+
+tbreak_and_cont "Start: rethrow"
+verify_testval "post-check - next past catch" 3
+gdb_test "next" ".*" "next over a throw 4"
+tbreak_and_cont "End: rethrow"
+verify_testval "pre-check - next over a throw 4" 3
+
+tbreak_and_cont "Start: first finish"
+verify_testval "post-check - next over a throw 4" 4
+gdb_test "step" "function1().*" "step into function2 2"
+gdb_test "finish" ".*" "finish 1"
+tbreak_and_cont "End: first finish"
+verify_testval "pre-check - finish 1" 4
+
+tbreak_and_cont "Start: second finish"
+verify_testval "post-check - finish 1" 5
+gdb_test "step" "function1 ().*" "step into finish method"
+gdb_test "finish" ".*" "finish 2"
+tbreak_and_cont "End: second finish"
+verify_testval "pre-check - finish 2" 5
+
+tbreak_and_cont "Start: first until"
+verify_testval "post-check - finish 2" 6
+gdb_test "step" ".*" "step into finish, for until"
+gdb_test "until" ".*" "until with no argument 1"
+set line [gdb_get_line_number "marker for until" $testfile.cc]
+gdb_test "until $line" "function1 ().*" "next past catch 6"
+gdb_test "until" ".*" "until with no argument 2"
+tbreak_and_cont "End: first until"
+verify_testval "pre-check - until 1" 6
+
+tbreak_and_cont "Start: second until"
+verify_testval "post-check - until 1" 7
+set line [gdb_get_line_number "until here" $testfile.cc]
+gdb_test "step" "function1 ().*" "step into until"
+gdb_test "until $line" ".*" "until-over-throw"
+tbreak_and_cont "End: second until"
+verify_testval "pre-check - until 2" 7
+
+tbreak_and_cont "Start: advance"
+verify_testval "post-check - until 2" 8
+gdb_test "step" "function1 ().*" "step into until, for advance"
+gdb_test "advance $line" ".*" "advance-over-throw"
+tbreak_and_cont "End: advance"
+verify_testval "pre-check - advance" 8
+
+tbreak_and_cont "done"
+verify_testval "post-check - advance" 9
diff --git a/gdb/testsuite/gdb.java/jnpe.exp b/gdb/testsuite/gdb.java/jnpe.exp
new file mode 100644
index 0000000..f2895f6
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.exp
@@ -0,0 +1,74 @@
+# Copyright 2009, 2010 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+load_lib "java.exp"
+
+set testfile "jnpe"
+set srcfile ${testfile}.java
+set binfile ${objdir}/${subdir}/${testfile}
+if { [compile_java_from_source ${srcdir}/$subdir/${srcfile} ${binfile} "-g"] != "" } {
+ untested "Couldn't compile ${srcdir}/$subdir/${srcfile}"
+ return -1
+}
+
+# Start with a fresh gdb.
+
+clean_restart $testfile
+
+set line [gdb_get_line_number "break here" $testfile.java]
+if ![runto "$testfile.java:$line"] then {
+ perror "couldn't run to jnpe.main"
+ continue
+}
+
+# See whether we have the needed unwinder hooks.
+set ok 1
+gdb_test_multiple "print _Unwind_DebugHook" "check for unwinder hook in java" {
+ -re "= .*_Unwind_DebugHook.*\r\n$gdb_prompt $" {
+ pass "check for unwinder hook in java"
+ }
+ -re "No symbol .* in current context.?\r\n$gdb_prompt $" {
+ # Pass the test so we don't get bogus fails in the results.
+ setup_xfail *-*-*
+ fail "check for unwinder hook in java"
+ set ok 0
+ }
+}
+if {!$ok} {
+ unsupported "jnpe.exp could not find _Unwind_DebugHook"
+ return -1
+}
+
+gdb_test "handle SIGSEGV nostop noprint" \
+ "SIGSEGV.*fault" \
+ "disable SIGSEGV for next-over-NPE"
+
+# The line where we stop differs according to gcj; just check that we
+# did not already execute the catch point. This is done in a somewhat
+# funny way due to other gcj debuginfo oddities that don't
+# meaningfully affect the user's experience.
+
+gdb_test "next" \
+ ".*" \
+ "next over NPE"
+
+set line [gdb_get_line_number "stop point"]
+gdb_breakpoint $line
+gdb_test "continue" "Continuing.\[\r\n\]*success\[\r\n\]*Breakpoint .*:$line\[\r\n\]*.*// stop point\[\r\n\]*" \
+ "continue to success for next-over-NPE"
diff --git a/gdb/testsuite/gdb.java/jnpe.java b/gdb/testsuite/gdb.java/jnpe.java
new file mode 100644
index 0000000..be096ce
--- /dev/null
+++ b/gdb/testsuite/gdb.java/jnpe.java
@@ -0,0 +1,40 @@
+// Test next-over-NPE.
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2009, 2010 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+public class jnpe
+{
+ public static String npe ()
+ {
+ return ((Object) null).toString();
+ }
+
+ public static void main (String[] args)
+ {
+ try
+ {
+ System.out.println (npe ()); // break here
+ }
+ catch (NullPointerException n)
+ {
+ System.out.println ("success");
+ }
+
+ System.out.println ("blah"); // stop point
+ }
+}
diff --git a/gdb/thread.c b/gdb/thread.c
index 2fdc7f9..40ed6d9 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -90,6 +90,16 @@ delete_step_resume_breakpoint (struct thread_info *tp)
}
}
+void
+delete_exception_resume_breakpoint (struct thread_info *tp)
+{
+ if (tp && tp->exception_resume_breakpoint)
+ {
+ delete_breakpoint (tp->exception_resume_breakpoint);
+ tp->exception_resume_breakpoint = NULL;
+ }
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
@@ -103,10 +113,18 @@ clear_thread_inferior_resources (struct thread_info *tp)
tp->step_resume_breakpoint = NULL;
}
+ if (tp->exception_resume_breakpoint)
+ {
+ tp->exception_resume_breakpoint->disposition = disp_del_at_next_stop;
+ tp->exception_resume_breakpoint = NULL;
+ }
+
bpstat_clear (&tp->stop_bpstat);
discard_all_intermediate_continuations_thread (tp);
discard_all_continuations_thread (tp);
+
+ delete_longjmp_breakpoint (tp->num);
}
static void
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-12-02 15:32 ` Tom Tromey
@ 2010-12-09 16:37 ` Tom Tromey
2010-12-10 4:52 ` Jan Kratochvil
0 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2010-12-09 16:37 UTC (permalink / raw)
To: gdb-patches
>>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
This is my second post. The first one was rejected by sourceware for
being too spammy.
Tom> Here is an updated version of the next-over-throw patch.
Tom> I think I've addressed all of Jan's comments.
Tom> Built and regtested on x86-64 (compile farm).
Tom> I also did various tests on PPC32 and PPC64 machines.
Tom> Let me know what you think.
Tom> In the absence of comments I will check this in next week.
I rebased this patch on CVS HEAD and committed it.
I'm appending the updated version.
I built and regtested this version as well.
Tom
2010-12-09 Tom Tromey <tromey@redhat.com>
PR c++/9593:
* thread.c (clear_thread_inferior_resources): Call
delete_longjmp_breakpoint.
* infrun.c (handle_inferior_event): Handle exception breakpoints.
(handle_inferior_event): Likewise.
(insert_exception_resume_breakpoint): New function.
(check_exception_resume): Likewise.
* inferior.h (delete_longjmp_breakpoint_cleanup): Declare.
* infcmd.c (delete_longjmp_breakpoint_cleanup): No longer static.
(step_1): Set thread's initiating frame.
(until_next_continuation): New function.
(until_next_command): Support exception breakpoints.
(finish_command_continuation): Delete longjmp breakpoint.
(finish_forward): Support exception breakpoints.
* gdbthread.h (struct thread_info) <initiating_frame>: New field.
* breakpoint.h (enum bptype) <bp_exception, bp_exception_resume,
bp_exception_master>: New constants.
(struct bpstat_what) <is_longjmp>: New field.
(set_longjmp_breakpoint): Update.
* breakpoint.c (create_exception_master_breakpoint): New function.
(update_breakpoints_after_exec): Handle bp_exception_master. Call
create_exception_master_breakpoint.
(print_it_typical): Handle bp_exception_master, bp_exception.
(bpstat_stop_status): Handle bp_exception_master.
(bpstat_what): Handle bp_exception_master, bp_exception,
bp_exception_resume.
(bptype_string): Likewise.
(print_one_breakpoint_location): Likewise.
(allocate_bp_location): Likewise.
(set_longjmp_breakpoint): Handle exception breakpoints. Change
interface.
(delete_longjmp_breakpoint): Handle exception breakpoints.
(mention): Likewise.
(struct until_break_command_continuation_args) <thread_num>: New
field.
(until_break_command_continuation): Call
delete_longjmp_breakpoint.
(until_break_command): Support exception breakpoints.
(delete_command): Likewise.
(breakpoint_re_set_one): Likewise.
(breakpoint_re_set): Likewise.
2010-12-09 Tom Tromey <tromey@redhat.com>
* gdb.java/jnpe.java: New file.
* gdb.java/jnpe.exp: New file.
* gdb.cp/nextoverthrow.exp: New file.
* gdb.cp/nextoverthrow.cc: New file.
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 1d66eef..6a51a3b 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -2209,6 +2209,35 @@ create_std_terminate_master_breakpoint (const char *func_name)
do_cleanups (old_chain);
}
+/* Install a master breakpoint on the unwinder's debug hook. */
+
+void
+create_exception_master_breakpoint (void)
+{
+ struct objfile *objfile;
+
+ ALL_OBJFILES (objfile)
+ {
+ struct minimal_symbol *debug_hook;
+
+ debug_hook = lookup_minimal_symbol ("_Unwind_DebugHook", NULL, objfile);
+ if (debug_hook != NULL)
+ {
+ struct breakpoint *b;
+ CORE_ADDR addr = SYMBOL_VALUE_ADDRESS (debug_hook);
+ struct gdbarch *gdbarch = get_objfile_arch (objfile);
+
+ addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr,
+ ¤t_target);
+ b = create_internal_breakpoint (gdbarch, addr, bp_exception_master);
+ b->addr_string = xstrdup ("_Unwind_DebugHook");
+ b->enable_state = bp_disabled;
+ }
+ }
+
+ update_global_location_list (1);
+}
+
void
update_breakpoints_after_exec (void)
{
@@ -2250,7 +2279,8 @@ update_breakpoints_after_exec (void)
/* Thread event breakpoints must be set anew after an exec(),
as must overlay event and longjmp master breakpoints. */
if (b->type == bp_thread_event || b->type == bp_overlay_event
- || b->type == bp_longjmp_master || b->type == bp_std_terminate_master)
+ || b->type == bp_longjmp_master || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
{
delete_breakpoint (b);
continue;
@@ -2265,7 +2295,8 @@ update_breakpoints_after_exec (void)
/* Longjmp and longjmp-resume breakpoints are also meaningless
after an exec. */
- if (b->type == bp_longjmp || b->type == bp_longjmp_resume)
+ if (b->type == bp_longjmp || b->type == bp_longjmp_resume
+ || b->type == bp_exception || b->type == bp_exception_resume)
{
delete_breakpoint (b);
continue;
@@ -2327,6 +2358,7 @@ update_breakpoints_after_exec (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
int
@@ -3248,6 +3280,12 @@ print_it_typical (bpstat bs)
result = PRINT_NOTHING;
break;
+ case bp_exception_master:
+ /* These should never be enabled. */
+ printf_filtered (_("Exception Master Breakpoint: gdb should not stop!\n"));
+ result = PRINT_NOTHING;
+ break;
+
case bp_watchpoint:
case bp_hardware_watchpoint:
annotate_watchpoint (b->number);
@@ -3335,6 +3373,8 @@ print_it_typical (bpstat bs)
case bp_none:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4121,7 +4161,8 @@ bpstat_stop_status (struct address_space *aspace,
if (b->type == bp_thread_event || b->type == bp_overlay_event
|| b->type == bp_longjmp_master
- || b->type == bp_std_terminate_master)
+ || b->type == bp_std_terminate_master
+ || b->type == bp_exception_master)
/* We do not stop for these. */
bs->stop = 0;
else
@@ -4216,6 +4257,7 @@ bpstat_what (bpstat bs)
retval.main_action = BPSTAT_WHAT_KEEP_CHECKING;
retval.call_dummy = STOP_NONE;
+ retval.is_longjmp = 0;
for (; bs != NULL; bs = bs->next)
{
@@ -4271,10 +4313,14 @@ bpstat_what (bpstat bs)
}
break;
case bp_longjmp:
+ case bp_exception:
this_action = BPSTAT_WHAT_SET_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp;
break;
case bp_longjmp_resume:
+ case bp_exception_resume:
this_action = BPSTAT_WHAT_CLEAR_LONGJMP_RESUME;
+ retval.is_longjmp = bptype == bp_longjmp_resume;
break;
case bp_step_resume:
if (bs->stop)
@@ -4290,6 +4336,7 @@ bpstat_what (bpstat bs)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
this_action = BPSTAT_WHAT_SINGLE;
break;
case bp_catchpoint:
@@ -4483,6 +4530,8 @@ bptype_string (enum bptype type)
{bp_access_watchpoint, "acc watchpoint"},
{bp_longjmp, "longjmp"},
{bp_longjmp_resume, "longjmp resume"},
+ {bp_exception, "exception"},
+ {bp_exception_resume, "exception resume"},
{bp_step_resume, "step resume"},
{bp_watchpoint_scope, "watchpoint scope"},
{bp_call_dummy, "call dummy"},
@@ -4492,6 +4541,7 @@ bptype_string (enum bptype type)
{bp_overlay_event, "overlay events"},
{bp_longjmp_master, "longjmp master"},
{bp_std_terminate_master, "std::terminate master"},
+ {bp_exception_master, "exception master"},
{bp_catchpoint, "catchpoint"},
{bp_tracepoint, "tracepoint"},
{bp_fast_tracepoint, "fast tracepoint"},
@@ -4630,6 +4680,8 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -4639,6 +4691,7 @@ print_one_breakpoint_location (struct breakpoint *b,
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
case bp_tracepoint:
case bp_fast_tracepoint:
case bp_static_tracepoint:
@@ -5379,6 +5432,8 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_watchpoint_scope:
case bp_call_dummy:
@@ -5389,6 +5444,7 @@ allocate_bp_location (struct breakpoint *bpt)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
loc->loc_type = bp_loc_software_breakpoint;
break;
case bp_hardware_breakpoint:
@@ -5605,13 +5661,14 @@ make_breakpoint_permanent (struct breakpoint *b)
}
/* Call this routine when stepping and nexting to enable a breakpoint
- if we do a longjmp() in THREAD. When we hit that breakpoint, call
- set_longjmp_resume_breakpoint() to figure out where we are going. */
+ if we do a longjmp() or 'throw' in TP. FRAME is the frame which
+ initiated the operation. */
void
-set_longjmp_breakpoint (int thread)
+set_longjmp_breakpoint (struct thread_info *tp, struct frame_id frame)
{
struct breakpoint *b, *temp;
+ int thread = tp->num;
/* To avoid having to rescan all objfile symbols at every step,
we maintain a list of continually-inserted but always disabled
@@ -5619,13 +5676,16 @@ set_longjmp_breakpoint (int thread)
clones of those and enable them for the requested thread. */
ALL_BREAKPOINTS_SAFE (b, temp)
if (b->pspace == current_program_space
- && b->type == bp_longjmp_master)
+ && (b->type == bp_longjmp_master
+ || b->type == bp_exception_master))
{
struct breakpoint *clone = clone_momentary_breakpoint (b);
- clone->type = bp_longjmp;
+ clone->type = b->type == bp_longjmp_master ? bp_longjmp : bp_exception;
clone->thread = thread;
}
+
+ tp->initiating_frame = frame;
}
/* Delete all longjmp breakpoints from THREAD. */
@@ -5635,7 +5695,7 @@ delete_longjmp_breakpoint (int thread)
struct breakpoint *b, *temp;
ALL_BREAKPOINTS_SAFE (b, temp)
- if (b->type == bp_longjmp)
+ if (b->type == bp_longjmp || b->type == bp_exception)
{
if (b->thread == thread)
delete_breakpoint (b);
@@ -6807,6 +6867,8 @@ mention (struct breakpoint *b)
case bp_finish:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_step_resume:
case bp_call_dummy:
case bp_std_terminate:
@@ -6817,6 +6879,7 @@ mention (struct breakpoint *b)
case bp_jit_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
break;
}
@@ -8490,6 +8553,7 @@ struct until_break_command_continuation_args
{
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2;
+ int thread_num;
};
/* This function is called by fetch_inferior_event via the
@@ -8504,6 +8568,7 @@ until_break_command_continuation (void *arg)
delete_breakpoint (a->breakpoint);
if (a->breakpoint2)
delete_breakpoint (a->breakpoint2);
+ delete_longjmp_breakpoint (a->thread_num);
}
void
@@ -8515,6 +8580,8 @@ until_break_command (char *arg, int from_tty, int anywhere)
struct breakpoint *breakpoint;
struct breakpoint *breakpoint2 = NULL;
struct cleanup *old_chain;
+ int thread;
+ struct thread_info *tp;
clear_proceed_status ();
@@ -8553,6 +8620,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ tp = inferior_thread ();
+ thread = tp->num;
+
/* Keep within the current frame, or in frames called by the current
one. */
@@ -8565,6 +8635,9 @@ until_break_command (char *arg, int from_tty, int anywhere)
frame_unwind_caller_id (frame),
bp_until);
make_cleanup_delete_breakpoint (breakpoint2);
+
+ set_longjmp_breakpoint (tp, frame_unwind_caller_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
proceed (-1, TARGET_SIGNAL_DEFAULT, 0);
@@ -8581,6 +8654,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
args->breakpoint = breakpoint;
args->breakpoint2 = breakpoint2;
+ args->thread_num = thread;
discard_cleanups (old_chain);
add_continuation (inferior_thread (),
@@ -9820,6 +9894,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
{
breaks_to_delete = 1;
@@ -9841,6 +9916,7 @@ delete_command (char *arg, int from_tty)
&& b->type != bp_overlay_event
&& b->type != bp_longjmp_master
&& b->type != bp_std_terminate_master
+ && b->type != bp_exception_master
&& b->number >= 0)
delete_breakpoint (b);
}
@@ -10301,6 +10377,7 @@ breakpoint_re_set_one (void *bint)
case bp_overlay_event:
case bp_longjmp_master:
case bp_std_terminate_master:
+ case bp_exception_master:
delete_breakpoint (b);
break;
@@ -10324,6 +10401,8 @@ breakpoint_re_set_one (void *bint)
case bp_step_resume:
case bp_longjmp:
case bp_longjmp_resume:
+ case bp_exception:
+ case bp_exception_resume:
case bp_jit_event:
break;
}
@@ -10367,6 +10446,7 @@ breakpoint_re_set (void)
create_longjmp_master_breakpoint ("siglongjmp");
create_longjmp_master_breakpoint ("_siglongjmp");
create_std_terminate_master_breakpoint ("std::terminate()");
+ create_exception_master_breakpoint ();
}
\f
/* Reset the thread number of this breakpoint:
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 0fb6830..a044c6b 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -57,6 +57,13 @@ enum bptype
bp_longjmp, /* secret breakpoint to find longjmp() */
bp_longjmp_resume, /* secret breakpoint to escape longjmp() */
+ /* An internal breakpoint that is installed on the unwinder's
+ debug hook. */
+ bp_exception,
+ /* An internal breakpoint that is set at the point where an
+ exception will land. */
+ bp_exception_resume,
+
/* Used by wait_for_inferior for stepping over subroutine calls, for
stepping over signal handlers, and for skipping prologues. */
bp_step_resume,
@@ -126,6 +133,9 @@ enum bptype
/* Master copies of std::terminate breakpoints. */
bp_std_terminate_master,
+ /* Like bp_longjmp_master, but for exceptions. */
+ bp_exception_master,
+
bp_catchpoint,
bp_tracepoint,
@@ -665,6 +675,11 @@ struct bpstat_what
continuing from a call dummy without popping the frame is not a
useful one). */
enum stop_stack_kind call_dummy;
+
+ /* Used for BPSTAT_WHAT_SET_LONGJMP_RESUME and
+ BPSTAT_WHAT_CLEAR_LONGJMP_RESUME. True if we are handling a
+ longjmp, false if we are handling an exception. */
+ int is_longjmp;
};
/* The possible return values for print_bpstat, print_it_normal,
@@ -925,7 +940,8 @@ extern int detach_breakpoints (int);
this PSPACE anymore. */
extern void breakpoint_program_space_exit (struct program_space *pspace);
-extern void set_longjmp_breakpoint (int thread);
+extern void set_longjmp_breakpoint (struct thread_info *tp,
+ struct frame_id frame);
extern void delete_longjmp_breakpoint (int thread);
extern void enable_overlay_breakpoints (void);
diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
index c6a1d5c..c3a49ef 100644
--- a/gdb/gdbthread.h
+++ b/gdb/gdbthread.h
@@ -40,6 +40,9 @@ struct thread_control_state
/* Step-resume or longjmp-resume breakpoint. */
struct breakpoint *step_resume_breakpoint;
+ /* Exception-resume breakpoint. */
+ struct breakpoint *exception_resume_breakpoint;
+
/* Range to single step within.
If this is nonzero, respond to a single-step signal by continuing
@@ -207,6 +210,10 @@ struct thread_info
/* True if this thread has been explicitly requested to stop. */
int stop_requested;
+ /* The initiating frame of a nexting operation, used for deciding
+ which exceptions to intercept. */
+ struct frame_id initiating_frame;
+
/* Private data used by the target vector implementation. */
struct private_thread_info *private;
@@ -243,6 +250,9 @@ extern void delete_thread_silent (ptid_t);
/* Delete a step_resume_breakpoint from the thread database. */
extern void delete_step_resume_breakpoint (struct thread_info *);
+/* Delete an exception_resume_breakpoint from the thread database. */
+extern void delete_exception_resume_breakpoint (struct thread_info *);
+
/* Translate the integer thread id (GDB's homegrown id, not the system's)
into a "pid" (which may be overloaded with extra thread information). */
extern ptid_t thread_id_to_pid (int);
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 9664468..4016443 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -822,7 +822,7 @@ nexti_command (char *count_string, int from_tty)
step_1 (1, 1, count_string);
}
-static void
+void
delete_longjmp_breakpoint_cleanup (void *arg)
{
int thread = * (int *) arg;
@@ -862,10 +862,12 @@ step_1 (int skip_subroutines, int single_inst, char *count_string)
if (!single_inst || skip_subroutines) /* leave si command alone */
{
+ struct thread_info *tp = inferior_thread ();
+
if (in_thread_list (inferior_ptid))
thread = pid_to_thread_id (inferior_ptid);
- set_longjmp_breakpoint (thread);
+ set_longjmp_breakpoint (tp, get_frame_id (get_current_frame ()));
make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
}
@@ -1220,6 +1222,16 @@ signal_command (char *signum_exp, int from_tty)
proceed ((CORE_ADDR) -1, oursig, 0);
}
+/* A continuation callback for until_next_command. */
+
+static void
+until_next_continuation (void *arg)
+{
+ struct thread_info *tp = arg;
+
+ delete_longjmp_breakpoint (tp->num);
+}
+
/* Proceed until we reach a different source line with pc greater than
our current one or exit the function. We skip calls in both cases.
@@ -1236,6 +1248,8 @@ until_next_command (int from_tty)
struct symbol *func;
struct symtab_and_line sal;
struct thread_info *tp = inferior_thread ();
+ int thread = tp->num;
+ struct cleanup *old_chain;
clear_proceed_status ();
set_step_frame ();
@@ -1271,7 +1285,18 @@ until_next_command (int from_tty)
tp->step_multi = 0; /* Only one call to proceed */
+ set_longjmp_breakpoint (tp, get_frame_id (frame));
+ old_chain = make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 1);
+
+ if (target_can_async_p () && is_running (inferior_ptid))
+ {
+ discard_cleanups (old_chain);
+ add_continuation (tp, until_next_continuation, tp, NULL);
+ }
+ else
+ do_cleanups (old_chain);
}
static void
@@ -1464,6 +1489,7 @@ finish_command_continuation (void *arg)
if (bs != NULL && tp->control.proceed_to_finish)
observer_notify_normal_stop (bs, 1 /* print frame */);
delete_breakpoint (a->breakpoint);
+ delete_longjmp_breakpoint (inferior_thread ()->num);
}
static void
@@ -1548,6 +1574,7 @@ finish_forward (struct symbol *function, struct frame_info *frame)
struct breakpoint *breakpoint;
struct cleanup *old_chain;
struct finish_command_continuation_args *cargs;
+ int thread = tp->num;
sal = find_pc_line (get_frame_pc (frame), 0);
sal.pc = get_frame_pc (frame);
@@ -1558,6 +1585,9 @@ finish_forward (struct symbol *function, struct frame_info *frame)
old_chain = make_cleanup_delete_breakpoint (breakpoint);
+ set_longjmp_breakpoint (tp, get_frame_id (frame));
+ make_cleanup (delete_longjmp_breakpoint_cleanup, &thread);
+
/* We want stop_registers, please... */
tp->control.proceed_to_finish = 1;
cargs = xmalloc (sizeof (*cargs));
diff --git a/gdb/inferior.h b/gdb/inferior.h
index b23a03b..f80ecb5 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -279,6 +279,8 @@ extern void interrupt_target_command (char *args, int from_tty);
extern void interrupt_target_1 (int all_threads);
+extern void delete_longjmp_breakpoint_cleanup (void *arg);
+
extern void detach_command (char *, int);
extern void notice_new_inferior (ptid_t, int, int);
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1604e95..1bc00a4 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -45,6 +45,8 @@
#include "language.h"
#include "solib.h"
#include "main.h"
+#include "dictionary.h"
+#include "block.h"
#include "gdb_assert.h"
#include "mi/mi-common.h"
#include "event-top.h"
@@ -377,6 +379,7 @@ follow_fork (void)
parent thread structure's run control related fields, not just these.
Initialized to avoid "may be used uninitialized" warnings from gcc. */
struct breakpoint *step_resume_breakpoint = NULL;
+ struct breakpoint *exception_resume_breakpoint = NULL;
CORE_ADDR step_range_start = 0;
CORE_ADDR step_range_end = 0;
struct frame_id step_frame_id = { 0 };
@@ -429,6 +432,8 @@ follow_fork (void)
step_range_start = tp->control.step_range_start;
step_range_end = tp->control.step_range_end;
step_frame_id = tp->control.step_frame_id;
+ exception_resume_breakpoint
+ = clone_momentary_breakpoint (tp->control.exception_resume_breakpoint);
/* For now, delete the parent's sr breakpoint, otherwise,
parent/child sr breakpoints are considered duplicates,
@@ -439,6 +444,7 @@ follow_fork (void)
tp->control.step_range_start = 0;
tp->control.step_range_end = 0;
tp->control.step_frame_id = null_frame_id;
+ delete_exception_resume_breakpoint (tp);
}
parent = inferior_ptid;
@@ -481,6 +487,8 @@ follow_fork (void)
tp->control.step_range_start = step_range_start;
tp->control.step_range_end = step_range_end;
tp->control.step_frame_id = step_frame_id;
+ tp->control.exception_resume_breakpoint
+ = exception_resume_breakpoint;
}
else
{
@@ -534,6 +542,9 @@ follow_inferior_reset_breakpoints (void)
if (tp->control.step_resume_breakpoint)
breakpoint_re_set_thread (tp->control.step_resume_breakpoint);
+ if (tp->control.exception_resume_breakpoint)
+ breakpoint_re_set_thread (tp->control.exception_resume_breakpoint);
+
/* Reinsert all breakpoints in the child. The user may have set
breakpoints after catching the fork, in which case those
were never set in the child, but only in the parent. This makes
@@ -771,6 +782,7 @@ follow_exec (ptid_t pid, char *execd_pathname)
/* If there was one, it's gone now. We cannot truly step-to-next
statement through an exec(). */
th->control.step_resume_breakpoint = NULL;
+ th->control.exception_resume_breakpoint = NULL;
th->control.step_range_start = 0;
th->control.step_range_end = 0;
@@ -2219,6 +2231,8 @@ static void insert_step_resume_breakpoint_at_sal (struct gdbarch *gdbarch,
struct symtab_and_line sr_sal,
struct frame_id sr_id);
static void insert_longjmp_resume_breakpoint (struct gdbarch *, CORE_ADDR);
+static void check_exception_resume (struct execution_control_state *,
+ struct frame_info *, struct symbol *);
static void stop_stepping (struct execution_control_state *ecs);
static void prepare_to_wait (struct execution_control_state *ecs);
@@ -2340,6 +2354,7 @@ delete_step_resume_breakpoint_callback (struct thread_info *info, void *data)
return 0;
delete_step_resume_breakpoint (info);
+ delete_exception_resume_breakpoint (info);
return 0;
}
@@ -2364,6 +2379,7 @@ delete_step_thread_step_resume_breakpoint (void)
struct thread_info *tp = inferior_thread ();
delete_step_resume_breakpoint (tp);
+ delete_exception_resume_breakpoint (tp);
}
else
/* In all-stop mode, delete all step-resume and longjmp-resume
@@ -4112,23 +4128,33 @@ process_event_stop_test:
ecs->event_thread->stepping_over_breakpoint = 1;
- if (!gdbarch_get_longjmp_target_p (gdbarch)
- || !gdbarch_get_longjmp_target (gdbarch, frame, &jmp_buf_pc))
+ if (what.is_longjmp)
{
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog, "\
+ if (!gdbarch_get_longjmp_target_p (gdbarch)
+ || !gdbarch_get_longjmp_target (gdbarch,
+ frame, &jmp_buf_pc))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog, "\
infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
- keep_going (ecs);
- return;
- }
+ keep_going (ecs);
+ return;
+ }
- /* We're going to replace the current step-resume breakpoint
- with a longjmp-resume breakpoint. */
- delete_step_resume_breakpoint (ecs->event_thread);
+ /* We're going to replace the current step-resume breakpoint
+ with a longjmp-resume breakpoint. */
+ delete_step_resume_breakpoint (ecs->event_thread);
- /* Insert a breakpoint at resume address. */
- insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ /* Insert a breakpoint at resume address. */
+ insert_longjmp_resume_breakpoint (gdbarch, jmp_buf_pc);
+ }
+ else
+ {
+ struct symbol *func = get_frame_function (frame);
+ if (func)
+ check_exception_resume (ecs, frame, func);
+ }
keep_going (ecs);
return;
@@ -4137,9 +4163,54 @@ infrun: BPSTAT_WHAT_SET_LONGJMP_RESUME (!gdbarch_get_longjmp_target)\n");
fprintf_unfiltered (gdb_stdlog,
"infrun: BPSTAT_WHAT_CLEAR_LONGJMP_RESUME\n");
- gdb_assert (ecs->event_thread->control.step_resume_breakpoint
- != NULL);
- delete_step_resume_breakpoint (ecs->event_thread);
+ if (what.is_longjmp)
+ {
+ gdb_assert (ecs->event_thread->control.step_resume_breakpoint
+ != NULL);
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }
+ else
+ {
+ /* There are several cases to consider.
+
+ 1. The initiating frame no longer exists. In this case
+ we must stop, because the exception has gone too far.
+
+ 2. The initiating frame exists, and is the same as the
+ current frame. We stop, because the exception has been
+ caught.
+
+ 3. The initiating frame exists and is different from
+ the current frame. This means the exception has been
+ caught beneath the initiating frame, so keep going. */
+ struct frame_info *init_frame
+ = frame_find_by_id (ecs->event_thread->initiating_frame);
+
+ gdb_assert (ecs->event_thread->control.exception_resume_breakpoint
+ != NULL);
+ delete_exception_resume_breakpoint (ecs->event_thread);
+
+ if (init_frame)
+ {
+ struct frame_id current_id
+ = get_frame_id (get_current_frame ());
+ if (frame_id_eq (current_id,
+ ecs->event_thread->initiating_frame))
+ {
+ /* Case 2. Fall through. */
+ }
+ else
+ {
+ /* Case 3. */
+ keep_going (ecs);
+ return;
+ }
+ }
+
+ /* For Cases 1 and 2, remove the step-resume breakpoint,
+ if it exists. */
+ delete_step_resume_breakpoint (ecs->event_thread);
+ }
ecs->event_thread->control.stop_step = 1;
print_end_stepping_range_reason ();
@@ -5109,6 +5180,97 @@ insert_longjmp_resume_breakpoint (struct gdbarch *gdbarch, CORE_ADDR pc)
set_momentary_breakpoint_at_pc (gdbarch, pc, bp_longjmp_resume);
}
+/* Insert an exception resume breakpoint. TP is the thread throwing
+ the exception. The block B is the block of the unwinder debug hook
+ function. FRAME is the frame corresponding to the call to this
+ function. SYM is the symbol of the function argument holding the
+ target PC of the exception. */
+
+static void
+insert_exception_resume_breakpoint (struct thread_info *tp,
+ struct block *b,
+ struct frame_info *frame,
+ struct symbol *sym)
+{
+ struct gdb_exception e;
+
+ /* We want to ignore errors here. */
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ struct symbol *vsym;
+ struct value *value;
+ CORE_ADDR handler;
+ struct breakpoint *bp;
+
+ vsym = lookup_symbol (SYMBOL_LINKAGE_NAME (sym), b, VAR_DOMAIN, NULL);
+ value = read_var_value (vsym, frame);
+ /* If the value was optimized out, revert to the old behavior. */
+ if (! value_optimized_out (value))
+ {
+ handler = value_as_address (value);
+
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: exception resume at %lx\n",
+ (unsigned long) handler);
+
+ bp = set_momentary_breakpoint_at_pc (get_frame_arch (frame),
+ handler, bp_exception_resume);
+ bp->thread = tp->num;
+ inferior_thread ()->control.exception_resume_breakpoint = bp;
+ }
+ }
+}
+
+/* This is called when an exception has been intercepted. Check to
+ see whether the exception's destination is of interest, and if so,
+ set an exception resume breakpoint there. */
+
+static void
+check_exception_resume (struct execution_control_state *ecs,
+ struct frame_info *frame, struct symbol *func)
+{
+ struct gdb_exception e;
+
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ struct block *b;
+ struct dict_iterator iter;
+ struct symbol *sym;
+ int argno = 0;
+
+ /* The exception breakpoint is a thread-specific breakpoint on
+ the unwinder's debug hook, declared as:
+
+ void _Unwind_DebugHook (void *cfa, void *handler);
+
+ The CFA argument indicates the frame to which control is
+ about to be transferred. HANDLER is the destination PC.
+
+ We ignore the CFA and set a temporary breakpoint at HANDLER.
+ This is not extremely efficient but it avoids issues in gdb
+ with computing the DWARF CFA, and it also works even in weird
+ cases such as throwing an exception from inside a signal
+ handler. */
+
+ b = SYMBOL_BLOCK_VALUE (func);
+ ALL_BLOCK_SYMBOLS (b, iter, sym)
+ {
+ if (!SYMBOL_IS_ARGUMENT (sym))
+ continue;
+
+ if (argno == 0)
+ ++argno;
+ else
+ {
+ insert_exception_resume_breakpoint (ecs->event_thread,
+ b, frame, sym);
+ break;
+ }
+ }
+ }
+}
+
static void
stop_stepping (struct execution_control_state *ecs)
{
diff --git a/gdb/thread.c b/gdb/thread.c
index e23a784..1be325f 100644
--- a/gdb/thread.c
+++ b/gdb/thread.c
@@ -90,6 +90,16 @@ delete_step_resume_breakpoint (struct thread_info *tp)
}
}
+void
+delete_exception_resume_breakpoint (struct thread_info *tp)
+{
+ if (tp && tp->control.exception_resume_breakpoint)
+ {
+ delete_breakpoint (tp->control.exception_resume_breakpoint);
+ tp->control.exception_resume_breakpoint = NULL;
+ }
+}
+
static void
clear_thread_inferior_resources (struct thread_info *tp)
{
@@ -103,10 +113,19 @@ clear_thread_inferior_resources (struct thread_info *tp)
tp->control.step_resume_breakpoint = NULL;
}
+ if (tp->control.exception_resume_breakpoint)
+ {
+ tp->control.exception_resume_breakpoint->disposition
+ = disp_del_at_next_stop;
+ tp->control.exception_resume_breakpoint = NULL;
+ }
+
bpstat_clear (&tp->control.stop_bpstat);
discard_all_intermediate_continuations_thread (tp);
discard_all_continuations_thread (tp);
+
+ delete_longjmp_breakpoint (tp->num);
}
static void
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-12-09 16:37 ` Tom Tromey
@ 2010-12-10 4:52 ` Jan Kratochvil
2010-12-10 20:07 ` Tom Tromey
0 siblings, 1 reply; 40+ messages in thread
From: Jan Kratochvil @ 2010-12-10 4:52 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Thu, 09 Dec 2010 17:37:17 +0100, Tom Tromey wrote:
> >>>>> "Tom" == Tom Tromey <tromey@redhat.com> writes:
> I rebased this patch on CVS HEAD and committed it.
> I'm appending the updated version.
The attached testcase extension causes:
breakpoint.c:4393: internal-error: bpstat_what: unhandled bptype -1785358955
The new field `tp->control.exception_resume_breakpoint' should be probably
handled by existing save_infcall_control_state, restore_infcall_control_state
and discard_infcall_control_state somehow like step_resume_breakpoint is.
Thanks,
Jan
--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
using namespace std;
-void dummy ()
+int dummy ()
{
+ return 0;
}
class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
function1 (val); // until here
}
+ void resumebpt (int val)
+ {
+ try
+ {
+ throw val;
+ }
+ catch (int x)
+ {
+ dummy ();
+ }
+ }
+
};
NextOverThrowDerivates next_cases;
@@ -198,6 +211,18 @@ int main ()
testval = val; // End: advance
}
+ // Test of "resumebpt".
+ try
+ {
+ next_cases.resumebpt (10); // Start: resumebpt
+ next_cases.resumebpt (11); // Second: resumebpt
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: resumebpt
+ }
+
testval = 32; // done
}
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
tbreak_and_cont "End: advance"
verify_testval "pre-check - advance" 8
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
tbreak_and_cont "done"
verify_testval "post-check - advance" 9
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-12-10 4:52 ` Jan Kratochvil
@ 2010-12-10 20:07 ` Tom Tromey
2010-12-11 5:27 ` Jan Kratochvil
0 siblings, 1 reply; 40+ messages in thread
From: Tom Tromey @ 2010-12-10 20:07 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
Jan> The new field `tp->control.exception_resume_breakpoint' should be
Jan> probably handled by existing save_infcall_control_state,
Jan> restore_infcall_control_state and discard_infcall_control_state
Jan> somehow like step_resume_breakpoint is.
Thanks for the test case.
Here is what I am testing.
It just mirrors the existing step_resume_breakpoint code.
I think that this is the right thing to do -- stash any
exception-related breakpoint while doing the infcall, then restore it
if the infcall succeeds.
Tom
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1bc00a4..f72a18d 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -6379,6 +6379,7 @@ save_infcall_control_state (void)
inf_status->inferior_control = inf->control;
tp->control.step_resume_breakpoint = NULL;
+ tp->control.exception_resume_breakpoint = NULL;
/* Save original bpstat chain to INF_STATUS; replace it in TP with copy of
chain. If caller's caller is walking the chain, they'll be happier if we
@@ -6428,6 +6429,10 @@ restore_infcall_control_state (struct infcall_control_state *inf_status)
if (tp->control.step_resume_breakpoint)
tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
+ if (tp->control.exception_resume_breakpoint)
+ tp->control.exception_resume_breakpoint->disposition
+ = disp_del_at_next_stop;
+
/* Handle the bpstat_copy of the chain. */
bpstat_clear (&tp->control.stop_bpstat);
@@ -6476,6 +6481,10 @@ discard_infcall_control_state (struct infcall_control_state *inf_status)
inf_status->thread_control.step_resume_breakpoint->disposition
= disp_del_at_next_stop;
+ if (inf_status->thread_control.exception_resume_breakpoint)
+ inf_status->thread_control.exception_resume_breakpoint->disposition
+ = disp_del_at_next_stop;
+
/* See save_infcall_control_state for info on stop_bpstat. */
bpstat_clear (&inf_status->thread_control.stop_bpstat);
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.cc b/gdb/testsuite/gdb.cp/nextoverthrow.cc
index b25cb34..c3da516 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
using namespace std;
-void dummy ()
+int dummy ()
{
+ return 0;
}
class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
function1 (val); // until here
}
+ void resumebpt (int val)
+ {
+ try
+ {
+ throw val;
+ }
+ catch (int x)
+ {
+ dummy ();
+ }
+ }
+
};
NextOverThrowDerivates next_cases;
@@ -198,6 +211,18 @@ int main ()
testval = val; // End: advance
}
+ // Test of "resumebpt".
+ try
+ {
+ next_cases.resumebpt (10); // Start: resumebpt
+ next_cases.resumebpt (11); // Second: resumebpt
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: resumebpt
+ }
+
testval = 32; // done
}
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
index 960ea0d..c19674f 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
tbreak_and_cont "End: advance"
verify_testval "pre-check - advance" 8
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
tbreak_and_cont "done"
verify_testval "post-check - advance" 9
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-12-10 20:07 ` Tom Tromey
@ 2010-12-11 5:27 ` Jan Kratochvil
2010-12-15 21:18 ` Tom Tromey
0 siblings, 1 reply; 40+ messages in thread
From: Jan Kratochvil @ 2010-12-11 5:27 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
On Fri, 10 Dec 2010 21:07:02 +0100, Tom Tromey wrote:
> >>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
> Here is what I am testing.
OK. Included a ChangeLog entry.
Thanks,
Jan
gdb.testsuite/
2010-12-11 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.cp/nextoverthrow.cc (dummy): Return int 0.
(NextOverThrowDerivates) <resumebpt>: New.
(main): New block for test of "resumebpt".
* gdb.cp/nextoverthrow.exp: New block for test of "resumebpt".
--- a/gdb/testsuite/gdb.cp/nextoverthrow.cc
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.cc
@@ -19,8 +19,9 @@
using namespace std;
-void dummy ()
+int dummy ()
{
+ return 0;
}
class NextOverThrowDerivates
@@ -89,6 +90,18 @@ public:
function1 (val); // until here
}
+ void resumebpt (int val)
+ {
+ try
+ {
+ throw val;
+ }
+ catch (int x)
+ {
+ dummy ();
+ }
+ }
+
};
NextOverThrowDerivates next_cases;
@@ -198,6 +211,18 @@ int main ()
testval = val; // End: advance
}
+ // Test of "resumebpt".
+ try
+ {
+ next_cases.resumebpt (10); // Start: resumebpt
+ next_cases.resumebpt (11); // Second: resumebpt
+ }
+ catch (int val)
+ {
+ dummy ();
+ testval = val; // End: resumebpt
+ }
+
testval = 32; // done
}
diff --git a/gdb/testsuite/gdb.cp/nextoverthrow.exp b/gdb/testsuite/gdb.cp/nextoverthrow.exp
index 960ea0d..c19674f 100644
--- a/gdb/testsuite/gdb.cp/nextoverthrow.exp
+++ b/gdb/testsuite/gdb.cp/nextoverthrow.exp
@@ -149,5 +149,14 @@ gdb_test "advance $line" ".*" "advance-over-throw"
tbreak_and_cont "End: advance"
verify_testval "pre-check - advance" 8
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
tbreak_and_cont "done"
verify_testval "post-check - advance" 9
^ permalink raw reply [flat|nested] 40+ messages in thread* Re: RFC: next/finish/etc -vs- exceptions
2010-12-11 5:27 ` Jan Kratochvil
@ 2010-12-15 21:18 ` Tom Tromey
0 siblings, 0 replies; 40+ messages in thread
From: Tom Tromey @ 2010-12-15 21:18 UTC (permalink / raw)
To: Jan Kratochvil; +Cc: gdb-patches
>>>>> "Jan" == Jan Kratochvil <jan.kratochvil@redhat.com> writes:
Tom> Here is what I am testing.
Jan> OK. Included a ChangeLog entry.
Thanks.
I had to modify the test a little to make it pass with gcc svn trunk.
It seems the test was a bit confused about the line number it was on.
Here is what I am checking in.
Tom
2010-12-15 Tom Tromey <tromey@redhat.com>
* infrun.c (save_infcall_control_state): Handle the
exception-resume breakpoint.
(restore_infcall_control_state): Likewise.
(discard_infcall_control_state): Likewise.
2010-12-15 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.cp/nextoverthrow.cc (dummy): Return int 0.
(NextOverThrowDerivates) <resumebpt>: New.
(resumebpt_test): New.
(main): Call resumebpt_test.
* gdb.cp/nextoverthrow.exp: New block for test of "resumebpt".
Index: infrun.c
===================================================================
RCS file: /cvs/src/src/gdb/infrun.c,v
retrieving revision 1.461
diff -u -r1.461 infrun.c
--- infrun.c 9 Dec 2010 16:09:52 -0000 1.461
+++ infrun.c 15 Dec 2010 21:15:45 -0000
@@ -6379,6 +6379,7 @@
inf_status->inferior_control = inf->control;
tp->control.step_resume_breakpoint = NULL;
+ tp->control.exception_resume_breakpoint = NULL;
/* Save original bpstat chain to INF_STATUS; replace it in TP with copy of
chain. If caller's caller is walking the chain, they'll be happier if we
@@ -6428,6 +6429,10 @@
if (tp->control.step_resume_breakpoint)
tp->control.step_resume_breakpoint->disposition = disp_del_at_next_stop;
+ if (tp->control.exception_resume_breakpoint)
+ tp->control.exception_resume_breakpoint->disposition
+ = disp_del_at_next_stop;
+
/* Handle the bpstat_copy of the chain. */
bpstat_clear (&tp->control.stop_bpstat);
@@ -6476,6 +6481,10 @@
inf_status->thread_control.step_resume_breakpoint->disposition
= disp_del_at_next_stop;
+ if (inf_status->thread_control.exception_resume_breakpoint)
+ inf_status->thread_control.exception_resume_breakpoint->disposition
+ = disp_del_at_next_stop;
+
/* See save_infcall_control_state for info on stop_bpstat. */
bpstat_clear (&inf_status->thread_control.stop_bpstat);
Index: testsuite/gdb.cp/nextoverthrow.cc
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.cp/nextoverthrow.cc,v
retrieving revision 1.1
diff -u -r1.1 nextoverthrow.cc
--- testsuite/gdb.cp/nextoverthrow.cc 9 Dec 2010 16:09:53 -0000 1.1
+++ testsuite/gdb.cp/nextoverthrow.cc 15 Dec 2010 21:15:47 -0000
@@ -19,8 +19,9 @@
using namespace std;
-void dummy ()
+int dummy ()
{
+ return 0;
}
class NextOverThrowDerivates
@@ -89,10 +90,39 @@
function1 (val); // until here
}
+ void resumebpt (int val)
+ {
+ try
+ {
+ throw val;
+ }
+ catch (int x)
+ {
+ dummy ();
+ }
+ }
+
};
NextOverThrowDerivates next_cases;
+int
+resumebpt_test (int x)
+{
+ try
+ {
+ next_cases.resumebpt (x); // Start: resumebpt
+ next_cases.resumebpt (x + 1); // Second: resumebpt
+ }
+ catch (int val)
+ {
+ dummy ();
+ x = val;
+ }
+
+ return x;
+}
+
int main ()
{
int testval = -1;
@@ -198,6 +228,8 @@
testval = val; // End: advance
}
+ // Test of "resumebpt".
+ testval = resumebpt_test (10);
+
testval = 32; // done
}
-
Index: testsuite/gdb.cp/nextoverthrow.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.cp/nextoverthrow.exp,v
retrieving revision 1.1
diff -u -r1.1 nextoverthrow.exp
--- testsuite/gdb.cp/nextoverthrow.exp 9 Dec 2010 16:09:53 -0000 1.1
+++ testsuite/gdb.cp/nextoverthrow.exp 15 Dec 2010 21:15:47 -0000
@@ -149,5 +149,14 @@
tbreak_and_cont "End: advance"
verify_testval "pre-check - advance" 8
+tbreak_and_cont "Start: resumebpt"
+gdb_test "tbreak _Unwind_RaiseException"
+gdb_test "continue" "Temporary breakpoint.*" "continuing to _Unwind_RaiseException"
+gdb_test "finish" "Run till exit .*"
+gdb_test {set $retpc=$pc}
+gdb_test {break *$retpc if dummy ()}
+tbreak_and_cont "Second: resumebpt"
+gdb_test "next"
+
tbreak_and_cont "done"
-verify_testval "post-check - advance" 9
+verify_testval "post-check - advance" 10
^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2010-12-15 21:18 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-05-29 22:32 RFC: next/finish/etc -vs- exceptions Tom Tromey
2009-05-30 21:08 ` Doug Evans
2009-05-30 23:18 ` Tom Tromey
2009-06-10 16:13 ` Joel Brobecker
2009-06-10 16:50 ` Tom Tromey
2009-06-10 17:05 ` Pedro Alves
2009-06-10 17:13 ` Daniel Jacobowitz
2009-06-10 17:47 ` Pedro Alves
2009-06-10 18:32 ` Tom Tromey
2009-06-10 18:49 ` Pedro Alves
2009-06-12 20:49 ` Tom Tromey
2009-06-12 21:51 ` Pedro Alves
2009-06-12 22:07 ` Pedro Alves
2010-11-25 4:54 ` Jan Kratochvil
2009-06-11 14:45 ` Joel Brobecker
2009-06-11 15:47 ` Tom Tromey
2009-07-24 17:38 ` Tom Tromey
2009-07-24 18:54 ` Pedro Alves
2009-07-24 19:18 ` Tom Tromey
2009-09-09 18:56 ` Tom Tromey
2010-10-07 1:37 Tom Tromey
2010-11-24 17:53 ` Joel Brobecker
2010-11-24 18:24 ` Tom Tromey
2010-11-25 7:59 ` Jan Kratochvil
2010-11-27 17:25 ` Doug Evans
2010-11-28 8:29 ` Joel Brobecker
2010-11-30 16:43 ` Tom Tromey
2010-11-30 17:02 ` Jan Kratochvil
2010-11-30 17:15 ` Phil Muldoon
2010-11-30 20:15 ` Tom Tromey
2010-12-01 13:42 ` Jan Kratochvil
2010-12-01 21:40 ` Tom Tromey
2010-11-30 18:23 ` Tom Tromey
2010-11-30 18:55 ` Tom Tromey
2010-12-02 15:32 ` Tom Tromey
2010-12-09 16:37 ` Tom Tromey
2010-12-10 4:52 ` Jan Kratochvil
2010-12-10 20:07 ` Tom Tromey
2010-12-11 5:27 ` Jan Kratochvil
2010-12-15 21:18 ` Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox