Blame SOURCES/gdb-rhbz1261564-aarch64-hw-watchpoint-1of5.patch

01917d
http://sourceware.org/ml/gdb-patches/2015-02/msg00730.html
01917d
Subject: [PATCH 2/8] Teach GDB about targets that can tell whether a trap is a breakpoint event
01917d
01917d
The moribund locations heuristics are problematic.  This patch teaches
01917d
GDB about targets that can reliably tell whether a trap was caused by
01917d
a software or hardware breakpoint, and thus don't need moribund
01917d
locations, thus bypassing all the problems that mechanism has.
01917d
01917d
The non-stop-fair-events.exp test is frequently failing currently.
01917d
E.g., see https://sourceware.org/ml/gdb-testers/2015-q1/msg03148.html.
01917d
01917d
The root cause is a fundamental problem with moribund locations.  For
01917d
example, the stepped_breakpoint logic added by af48d08f breaks in this
01917d
case (which is what happens with that test):
01917d
01917d
 - Step thread A, no breakpoint is set at PC.
01917d
01917d
 - The kernel doesn't schedule thread A yet.
01917d
01917d
 - Insert breakpoint at A's PC, for some reason (e.g., a step-resume
01917d
   breakpoint for thread B).
01917d
01917d
 - Kernel finally schedules thread A.
01917d
01917d
 - thread A's stepped_breakpoint flag is not set, even though it now
01917d
   stepped a breakpoint instruction.
01917d
01917d
 - adjust_pc_after_break gets the PC wrong, because PC == PREV_PC, but
01917d
   stepped_breakpoint is not set.
01917d
01917d
We needed the stepped_breakpoint logic to workaround moribund
01917d
locations, because otherwise adjust_pc_after_break could apply an
01917d
adjustment when it shouldn't just because there _used_ to be a
01917d
breakpoint at PC (a moribund breakpoint location).  For example, on
01917d
x86, that's wrong if the thread really hasn't executed an int3, but
01917d
instead executed some other 1-byte long instruction.  Getting the PC
01917d
adjustment wrong of course leads to the inferior executing the wrong
01917d
instruction.
01917d
01917d
Other problems with moribund locations are:
01917d
01917d
 - if a true SIGTRAP happens to be raised when the program is
01917d
   executing the PC that used to have a breakpoint, GDB will assume
01917d
   that is a trap for a breakpoint that has recently been removed, and
01917d
   thus we miss reporting the random signal to the user.
01917d
01917d
 - to minimize that, we get rid of moribund location after a while.
01917d
   That while is defined as just a certain number of events being
01917d
   processed.  That number of events sometimes passes by before a
01917d
   delayed breakpoint is processed, and GDB confuses the trap for a
01917d
   random signal, thus reporting the random trap.  Once the user
01917d
   resumes the thread, the program crashes because the PC was not
01917d
   adjusted...
01917d
01917d
The fix for all this is to bite the bullet and get rid of heuristics
01917d
and instead rely on the target knowing accurately what caused the
01917d
SIGTRAP.  The target/kernel/stub is in the best position to know what
01917d
that, because it can e.g. consult priviledged CPU flags GDB has no
01917d
access to, or by knowing which exception vector entry was called when
01917d
the instruction trapped, etc.  Most debug APIs I've seen to date
01917d
report breakpoint hits as a distinct event in some fashion.  For
01917d
example, on the Linux kernel, whether a breakpoint was executed is
01917d
exposed to userspace in the si_code field of the SIGTRAP's siginfo.
01917d
On Windows, the debug API reports a EXCEPTION_BREAKPOINT exception
01917d
code.
01917d
01917d
We needed to keep around deleted breakpoints in an on-the-side list
01917d
(the moribund locations) for two main reasons:
01917d
01917d
  - Know that a SIGTRAP actually is a delayed event for a hit of a
01917d
    breakpoint that was removed before the event was processed, and
01917d
    thus should not be reported as a random signal.
01917d
01917d
  - So we still do the decr_pc_after_break adjustment in that case, so
01917d
    that the thread is resumed at the correct address.
01917d
01917d
In the new model, if GDB processes an event the target tells is a
01917d
breakpoint trap, and GDB doesn't find the corresponding breakpoint in
01917d
its breakpoint tables, it means that event is a delayed event for a
01917d
breakpoint that has since been removed, and thus the event should be
01917d
ignored.
01917d
01917d
For the decr_pc_after_after issue, it ends up being much simpler that
01917d
on targets that can reliably tell whether a breakpoint trapped, for
01917d
the breakpoint trap to present the PC already adjusted.  Proper
01917d
multi-threading support already implies that targets needs to be doing
01917d
decr_pc_after_break adjustment themselves, otherwise for example, in
01917d
all-stop if two threads hit a breakpoint simultaneously, and the user
01917d
does "info threads", he'll see the non-event thread that hit the
01917d
breakpoint stopped at the wrong PC.
01917d
01917d
This way (target adjusts) also ends up eliminating the need for some
01917d
awkward re-incrementing of the PC in the record-full and Linux targets
01917d
that we do today, and the need for the target_decr_pc_after_break
01917d
hook.
01917d
01917d
If the target always adjusts, then there's a case where GDB needs to
01917d
re-increment the PC.  Say, on x86, an "int3" instruction that was
01917d
explicitly written in the program traps.  In this case, GDB should
01917d
report a random SIGTRAP signal to the user, with the PC pointing at
01917d
the instruction past the int3, just like if GDB was not debugging the
01917d
program.  The user may well decide to pass the SIGTRAP to the program
01917d
because the program being debugged has a SIGTRAP handler that handles
01917d
its own breakpoints, and expects the PC to be unadjusted.
01917d
01917d
Tested on x86-64 Fedora 20.
01917d
01917d
gdb/ChangeLog:
01917d
2015-02-25  Pedro Alves  <palves@redhat.com>
01917d
01917d
	* breakpoint.c (need_moribund_for_location_type): New function.
01917d
	(bpstat_stop_status): Don't skipping checking moribund locations
01917d
	of breakpoint types which the target tell caused a stop.
01917d
	(program_breakpoint_here_p): New function, factored out from ...
01917d
	(bp_loc_is_permanent): ... this.
01917d
	(update_global_location_list): Don't create a moribund location if
01917d
	the target supports reporting stops of the type of the removed
01917d
	breakpoint.
01917d
	* breakpoint.h (program_breakpoint_here_p): New declaration.
01917d
	* infrun.c (adjust_pc_after_break): Return early if the target has
01917d
	already adjusted the PC.  Add comments.
01917d
	(handle_signal_stop): If nothing explains a signal, and the target
01917d
	tells us the stop was caused by a software breakpoint, check if
01917d
	there's a breakpoint instruction in the memory.  If so, adjust the
01917d
	PC before presenting the stop to the user.  Otherwise, ignore the
01917d
	trap.  If nothing explains a signal, and the target tells us the
01917d
	stop was caused by a hardware breakpoint, ignore the trap.
01917d
	* target.h (struct target_ops) 
01917d
	to_supports_stopped_by_sw_breakpoint, to_stopped_by_hw_breakpoint,
01917d
	to_supports_stopped_by_hw_breakpoint>: New fields.
01917d
	(target_stopped_by_sw_breakpoint)
01917d
	(target_supports_stopped_by_sw_breakpoint)
01917d
	(target_stopped_by_hw_breakpoint)
01917d
	(target_supports_stopped_by_hw_breakpoint): Define.
01917d
	* target-delegates.c: Regenerate.
01917d
---
01917d
 gdb/breakpoint.c       |  91 ++++++++++++++++++++++++------------
01917d
 gdb/breakpoint.h       |   5 ++
01917d
 gdb/infrun.c           |  70 +++++++++++++++++++++++++++-
01917d
 gdb/target-delegates.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
01917d
 gdb/target.h           |  44 ++++++++++++++++++
01917d
 5 files changed, 303 insertions(+), 31 deletions(-)
01917d
01917d
Index: gdb-7.6.1/gdb/target.h
01917d
===================================================================
01917d
--- gdb-7.6.1.orig/gdb/target.h	2016-03-13 22:01:58.833963789 +0100
01917d
+++ gdb-7.6.1/gdb/target.h	2016-03-13 22:01:59.950971809 +0100
01917d
@@ -462,6 +462,16 @@
01917d
     void (*to_files_info) (struct target_ops *);
01917d
     int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *);
01917d
     int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *);
01917d
+
01917d
+    /* Returns true if the target stopped for a hardware breakpoint.
01917d
+       Likewise, if the target supports hardware breakpoints, this
01917d
+       method is necessary for correct background execution / non-stop
01917d
+       mode operation.  Even though hardware breakpoints do not
01917d
+       require PC adjustment, GDB needs to be able to tell whether the
01917d
+       hardware breakpoint event is a delayed event for a breakpoint
01917d
+       that is already gone and should thus be ignored.  */
01917d
+    int (*to_stopped_by_hw_breakpoint) (struct target_ops *);
01917d
+
01917d
     int (*to_can_use_hw_breakpoint) (int, int, int);
01917d
     int (*to_ranged_break_num_registers) (struct target_ops *);
01917d
     int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
01917d
@@ -1548,6 +1558,9 @@
01917d
 #define target_stopped_by_watchpoint \
01917d
    (*current_target.to_stopped_by_watchpoint)
01917d
 
01917d
+#define target_stopped_by_hw_breakpoint()				\
01917d
+  ((*current_target.to_stopped_by_hw_breakpoint) (&current_target))
01917d
+
01917d
 /* Non-zero if we have steppable watchpoints  */
01917d
 
01917d
 #define target_have_steppable_watchpoint \
01917d
Index: gdb-7.6.1/gdb/infrun.c
01917d
===================================================================
01917d
--- gdb-7.6.1.orig/gdb/infrun.c	2016-03-13 22:01:58.704962863 +0100
01917d
+++ gdb-7.6.1/gdb/infrun.c	2016-03-13 22:17:33.735674697 +0100
01917d
@@ -4295,6 +4295,18 @@
01917d
 	ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_TRAP;
01917d
     }
01917d
 
01917d
+  /* Maybe this was a trap for a hardware breakpoint/watchpoint that
01917d
+     has since been removed.  */
01917d
+  if (ecs->random_signal && target_stopped_by_hw_breakpoint())
01917d
+    {
01917d
+      /* A delayed hardware breakpoint event.  Ignore the trap.  */
01917d
+      if (debug_infrun)
01917d
+	fprintf_unfiltered (gdb_stdlog,
01917d
+			    "infrun: delayed hardware breakpoint/watchpoint "
01917d
+			    "trap, ignoring\n");
01917d
+      ecs->random_signal = 0;
01917d
+    }
01917d
+
01917d
 process_event_stop_test:
01917d
 
01917d
   /* Re-fetch current thread's frame in case we did a
01917d
Index: gdb-7.6.1/gdb/target.c
01917d
===================================================================
01917d
--- gdb-7.6.1.orig/gdb/target.c	2016-03-13 22:01:58.832963782 +0100
01917d
+++ gdb-7.6.1/gdb/target.c	2016-03-13 22:17:20.367578754 +0100
01917d
@@ -726,6 +726,7 @@
01917d
       /* Do not inherit to_memory_map.  */
01917d
       /* Do not inherit to_flash_erase.  */
01917d
       /* Do not inherit to_flash_done.  */
01917d
+      INHERIT (to_stopped_by_hw_breakpoint, t);
01917d
     }
01917d
 #undef INHERIT
01917d
 
01917d
@@ -968,6 +969,9 @@
01917d
 	    (int (*) (void))
01917d
 	    return_zero);
01917d
   de_fault (to_execution_direction, default_execution_direction);
01917d
+  de_fault (to_stopped_by_hw_breakpoint,
01917d
+	    (int (*) (struct target_ops *))
01917d
+	    return_zero);
01917d
 
01917d
 #undef de_fault
01917d