if (!found_object)
{
- if (removed && non_stop)
+ if (removed && non_stop
+ && breakpoint_address_is_meaningful (old_loc->owner)
+ && !is_hardware_watchpoint (old_loc->owner))
{
- /* This location was removed from the targets. In non-stop mode,
- a race condition is possible where we've removed a breakpoint,
- but stop events for that breakpoint are already queued and will
- arrive later. To suppress spurious SIGTRAPs reported to user,
- we keep this breakpoint location for a bit, and will retire it
- after we see 3 * thread_count events.
- The theory here is that reporting of events should,
- "on the average", be fair, so after that many event we'll see
- events from all threads that have anything of interest, and no
- longer need to keep this breakpoint. This is just a
- heuristic, but if it's wrong, we'll report unexpected SIGTRAP,
- which is usability issue, but not a correctness problem. */
+ /* This location was removed from the target. In
+ non-stop mode, a race condition is possible where
+ we've removed a breakpoint, but stop events for that
+ breakpoint are already queued and will arrive later.
+ We apply an heuristic to be able to distinguish such
+ SIGTRAPs from other random SIGTRAPs: we keep this
+ breakpoint location for a bit, and will retire it
+ after we see some number of events. The theory here
+ is that reporting of events should, "on the average",
+ be fair, so after a while we'll see events from all
+ threads that have anything of interest, and no longer
+ need to keep this breakpoint location around. We
+ don't hold locations forever so to reduce chances of
+ mistaking a non-breakpoint SIGTRAP for a breakpoint
+ SIGTRAP.
+
+ The heuristic failing can be disastrous on
+ decr_pc_after_break targets.
+
+ On decr_pc_after_break targets, like e.g., x86-linux,
+ if we fail to recognize a late breakpoint SIGTRAP,
+ because events_till_retirement has reached 0 too
+ soon, we'll fail to do the PC adjustment, and report
+ a random SIGTRAP to the user. When the user resumes
+ the inferior, it will most likely immediately crash
+ with SIGILL/SIGBUS/SEGSEGV, or worse, get silently
+ corrupted, because of being resumed e.g., in the
+ middle of a multi-byte instruction, or skipped a
+ one-byte instruction. This was actually seen happen
+ on native x86-linux, and should be less rare on
+ targets that do not support new thread events, like
+ remote, due to the heuristic depending on
+ thread_count.
+
+ Mistaking a random SIGTRAP for a breakpoint trap
+ causes similar symptoms (PC adjustment applied when
+ it shouldn't), but then again, playing with SIGTRAPs
+ behind the debugger's back is asking for trouble.
+
+ Since hardware watchpoint traps are always
+ distinguishable from other traps, so we don't need to
+ apply keep hardware watchpoint moribund locations
+ around. We simply always ignore hardware watchpoint
+ traps we can no longer explain. */
+
old_loc->events_till_retirement = 3 * (thread_count () + 1);
old_loc->owner = NULL;
function. */
stop_print_frame = 1;
+ /* This is where we handle "moribund" watchpoints. Unlike
+ software breakpoints traps, hardware watchpoint traps are
+ always distinguishable from random traps. If no high-level
+ watchpoint is associated with the reported stop data address
+ anymore, then the bpstat does not explain the signal ---
+ simply make sure to ignore it if `stopped_by_watchpoint' is
+ set. */
+
+ if (debug_infrun
+ && ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP
+ && !bpstat_explains_signal (ecs->event_thread->stop_bpstat)
+ && stopped_by_watchpoint)
+ fprintf_unfiltered (gdb_stdlog, "\
+infrun: no user watchpoint explains watchpoint SIGTRAP, ignoring\n");
+
/* NOTE: cagney/2003-03-29: These two checks for a random signal
at one stage in the past included checks for an inferior
function call's call dummy's return breakpoint. The original
if (ecs->event_thread->stop_signal == TARGET_SIGNAL_TRAP)
ecs->random_signal
= !(bpstat_explains_signal (ecs->event_thread->stop_bpstat)
+ || stopped_by_watchpoint
|| ecs->event_thread->trap_expected
|| (ecs->event_thread->step_range_end
&& ecs->event_thread->step_resume_breakpoint == NULL));