This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] Teach GDB about targets that can tell whether a trap is a breakpoint event


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=1cf4d9513af10d419c71099ae644f07b6724642b

commit 1cf4d9513af10d419c71099ae644f07b6724642b
Author: Pedro Alves <palves@redhat.com>
Date:   Wed Mar 4 20:41:15 2015 +0000

    Teach GDB about targets that can tell whether a trap is a breakpoint event
    
    The moribund locations heuristics are problematic.  This patch teaches
    GDB about targets that can reliably tell whether a trap was caused by
    a software or hardware breakpoint, and thus don't need moribund
    locations, thus bypassing all the problems that mechanism has.
    
    The non-stop-fair-events.exp test is frequently failing currently.
    E.g., see https://sourceware.org/ml/gdb-testers/2015-q1/msg03148.html.
    
    The root cause is a fundamental problem with moribund locations.  For
    example, the stepped_breakpoint logic added by af48d08f breaks in this
    case (which is what happens with that test):
    
     - Step thread A, no breakpoint is set at PC.
    
     - The kernel doesn't schedule thread A yet.
    
     - Insert breakpoint at A's PC, for some reason (e.g., a step-resume
       breakpoint for thread B).
    
     - Kernel finally schedules thread A.
    
     - thread A's stepped_breakpoint flag is not set, even though it now
       stepped a breakpoint instruction.
    
     - adjust_pc_after_break gets the PC wrong, because PC == PREV_PC, but
       stepped_breakpoint is not set.
    
    We needed the stepped_breakpoint logic to workaround moribund
    locations, because otherwise adjust_pc_after_break could apply an
    adjustment when it shouldn't just because there _used_ to be a
    breakpoint at PC (a moribund breakpoint location).  For example, on
    x86, that's wrong if the thread really hasn't executed an int3, but
    instead executed some other 1-byte long instruction.  Getting the PC
    adjustment wrong of course leads to the inferior executing the wrong
    instruction.
    
    Other problems with moribund locations are:
    
     - if a true SIGTRAP happens to be raised when the program is
       executing the PC that used to have a breakpoint, GDB will assume
       that is a trap for a breakpoint that has recently been removed, and
       thus we miss reporting the random signal to the user.
    
     - to minimize that, we get rid of moribund location after a while.
       That while is defined as just a certain number of events being
       processed.  That number of events sometimes passes by before a
       delayed breakpoint is processed, and GDB confuses the trap for a
       random signal, thus reporting the random trap.  Once the user
       resumes the thread, the program crashes because the PC was not
       adjusted...
    
    The fix for all this is to bite the bullet and get rid of heuristics
    and instead rely on the target knowing accurately what caused the
    SIGTRAP.  The target/kernel/stub is in the best position to know what
    that, because it can e.g. consult priviledged CPU flags GDB has no
    access to, or by knowing which exception vector entry was called when
    the instruction trapped, etc.  Most debug APIs I've seen to date
    report breakpoint hits as a distinct event in some fashion.  For
    example, on the Linux kernel, whether a breakpoint was executed is
    exposed to userspace in the si_code field of the SIGTRAP's siginfo.
    On Windows, the debug API reports a EXCEPTION_BREAKPOINT exception
    code.
    
    We needed to keep around deleted breakpoints in an on-the-side list
    (the moribund locations) for two main reasons:
    
      - Know that a SIGTRAP actually is a delayed event for a hit of a
        breakpoint that was removed before the event was processed, and
        thus should not be reported as a random signal.
    
      - So we still do the decr_pc_after_break adjustment in that case, so
        that the thread is resumed at the correct address.
    
    In the new model, if GDB processes an event the target tells is a
    breakpoint trap, and GDB doesn't find the corresponding breakpoint in
    its breakpoint tables, it means that event is a delayed event for a
    breakpoint that has since been removed, and thus the event should be
    ignored.
    
    For the decr_pc_after_after issue, it ends up being much simpler that
    on targets that can reliably tell whether a breakpoint trapped, for
    the breakpoint trap to present the PC already adjusted.  Proper
    multi-threading support already implies that targets needs to be doing
    decr_pc_after_break adjustment themselves, otherwise for example, in
    all-stop if two threads hit a breakpoint simultaneously, and the user
    does "info threads", he'll see the non-event thread that hit the
    breakpoint stopped at the wrong PC.
    
    This way (target adjusts) also ends up eliminating the need for some
    awkward re-incrementing of the PC in the record-full and Linux targets
    that we do today, and the need for the target_decr_pc_after_break
    hook.
    
    If the target always adjusts, then there's a case where GDB needs to
    re-increment the PC.  Say, on x86, an "int3" instruction that was
    explicitly written in the program traps.  In this case, GDB should
    report a random SIGTRAP signal to the user, with the PC pointing at
    the instruction past the int3, just like if GDB was not debugging the
    program.  The user may well decide to pass the SIGTRAP to the program
    because the program being debugged has a SIGTRAP handler that handles
    its own breakpoints, and expects the PC to be unadjusted.
    
    Tested on x86-64 Fedora 20.
    
    gdb/ChangeLog:
    2015-03-04  Pedro Alves  <palves@redhat.com>
    
    	* breakpoint.c (need_moribund_for_location_type): New function.
    	(bpstat_stop_status): Don't skipping checking moribund locations
    	of breakpoint types which the target tell caused a stop.
    	(program_breakpoint_here_p): New function, factored out from ...
    	(bp_loc_is_permanent): ... this.
    	(update_global_location_list): Don't create a moribund location if
    	the target supports reporting stops of the type of the removed
    	breakpoint.
    	* breakpoint.h (program_breakpoint_here_p): New declaration.
    	* infrun.c (adjust_pc_after_break): Return early if the target has
    	already adjusted the PC.  Add comments.
    	(handle_signal_stop): If nothing explains a signal, and the target
    	tells us the stop was caused by a software breakpoint, check if
    	there's a breakpoint instruction in the memory.  If so, adjust the
    	PC before presenting the stop to the user.  Otherwise, ignore the
    	trap.  If nothing explains a signal, and the target tells us the
    	stop was caused by a hardware breakpoint, ignore the trap.
    	* target.h (struct target_ops) <to_stopped_by_sw_breakpoint,
    	to_supports_stopped_by_sw_breakpoint, to_stopped_by_hw_breakpoint,
    	to_supports_stopped_by_hw_breakpoint>: New fields.
    	(target_stopped_by_sw_breakpoint)
    	(target_supports_stopped_by_sw_breakpoint)
    	(target_stopped_by_hw_breakpoint)
    	(target_supports_stopped_by_hw_breakpoint): Define.
    	* target-delegates.c: Regenerate.

Diff:
---
 gdb/ChangeLog          |  28 +++++++++++
 gdb/breakpoint.c       |  91 ++++++++++++++++++++++++------------
 gdb/breakpoint.h       |   5 ++
 gdb/infrun.c           |  70 +++++++++++++++++++++++++++-
 gdb/target-delegates.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/target.h           |  44 ++++++++++++++++++
 6 files changed, 331 insertions(+), 31 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6ed6f4a..59fcf9a 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,33 @@
 2015-03-04  Pedro Alves  <palves@redhat.com>
 
+	* breakpoint.c (need_moribund_for_location_type): New function.
+	(bpstat_stop_status): Don't skipping checking moribund locations
+	of breakpoint types which the target tell caused a stop.
+	(program_breakpoint_here_p): New function, factored out from ...
+	(bp_loc_is_permanent): ... this.
+	(update_global_location_list): Don't create a moribund location if
+	the target supports reporting stops of the type of the removed
+	breakpoint.
+	* breakpoint.h (program_breakpoint_here_p): New declaration.
+	* infrun.c (adjust_pc_after_break): Return early if the target has
+	already adjusted the PC.  Add comments.
+	(handle_signal_stop): If nothing explains a signal, and the target
+	tells us the stop was caused by a software breakpoint, check if
+	there's a breakpoint instruction in the memory.  If so, adjust the
+	PC before presenting the stop to the user.  Otherwise, ignore the
+	trap.  If nothing explains a signal, and the target tells us the
+	stop was caused by a hardware breakpoint, ignore the trap.
+	* target.h (struct target_ops) <to_stopped_by_sw_breakpoint,
+	to_supports_stopped_by_sw_breakpoint, to_stopped_by_hw_breakpoint,
+	to_supports_stopped_by_hw_breakpoint>: New fields.
+	(target_stopped_by_sw_breakpoint)
+	(target_supports_stopped_by_sw_breakpoint)
+	(target_stopped_by_hw_breakpoint)
+	(target_supports_stopped_by_hw_breakpoint): Define.
+	* target-delegates.c: Regenerate.
+
+2015-03-04  Pedro Alves  <palves@redhat.com>
+
 	* infrun.c (follow_fork_inferior): Use the whole of the
 	inferior_ptid and pending_follow.related_pid ptids instead of
 	building ptids from the process components.  Adjust verbose output
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index db4b872..c5d3240 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -5452,6 +5452,18 @@ bpstat_check_breakpoint_conditions (bpstat bs, ptid_t ptid)
     }	
 }
 
+/* Returns true if we need to track moribund locations of LOC's type
+   on the current target.  */
+
+static int
+need_moribund_for_location_type (struct bp_location *loc)
+{
+  return ((loc->loc_type == bp_loc_software_breakpoint
+	   && !target_supports_stopped_by_sw_breakpoint ())
+	  || (loc->loc_type == bp_loc_hardware_breakpoint
+	      && !target_supports_stopped_by_hw_breakpoint ()));
+}
+
 
 /* Get a bpstat associated with having just stopped at address
    BP_ADDR in thread PTID.
@@ -5541,15 +5553,20 @@ bpstat_stop_status (struct address_space *aspace,
     }
 
   /* Check if a moribund breakpoint explains the stop.  */
-  for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
+  if (!target_supports_stopped_by_sw_breakpoint ()
+      || !target_supports_stopped_by_hw_breakpoint ())
     {
-      if (breakpoint_location_address_match (loc, aspace, bp_addr))
+      for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
 	{
-	  bs = bpstat_alloc (loc, &bs_link);
-	  /* For hits of moribund locations, we should just proceed.  */
-	  bs->stop = 0;
-	  bs->print = 0;
-	  bs->print_it = print_it_noop;
+	  if (breakpoint_location_address_match (loc, aspace, bp_addr)
+	      && need_moribund_for_location_type (loc))
+	    {
+	      bs = bpstat_alloc (loc, &bs_link);
+	      /* For hits of moribund locations, we should just proceed.  */
+	      bs->stop = 0;
+	      bs->print = 0;
+	      bs->print_it = print_it_noop;
+	    }
 	}
     }
 
@@ -9304,11 +9321,10 @@ add_location_to_breakpoint (struct breakpoint *b,
 }
 
 
-/* Return 1 if LOC is pointing to a permanent breakpoint, 
-   return 0 otherwise.  */
+/* See breakpoint.h.  */
 
-static int
-bp_loc_is_permanent (struct bp_location *loc)
+int
+program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address)
 {
   int len;
   CORE_ADDR addr;
@@ -9317,6 +9333,38 @@ bp_loc_is_permanent (struct bp_location *loc)
   struct cleanup *cleanup;
   int retval = 0;
 
+  addr = address;
+  bpoint = gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  /* Software breakpoints unsupported?  */
+  if (bpoint == NULL)
+    return 0;
+
+  target_mem = alloca (len);
+
+  /* Enable the automatic memory restoration from breakpoints while
+     we read the memory.  Otherwise we could say about our temporary
+     breakpoints they are permanent.  */
+  cleanup = make_show_memory_breakpoints_cleanup (0);
+
+  if (target_read_memory (address, target_mem, len) == 0
+      && memcmp (target_mem, bpoint, len) == 0)
+    retval = 1;
+
+  do_cleanups (cleanup);
+
+  return retval;
+}
+
+/* Return 1 if LOC is pointing to a permanent breakpoint,
+   return 0 otherwise.  */
+
+static int
+bp_loc_is_permanent (struct bp_location *loc)
+{
+  struct cleanup *cleanup;
+  int retval;
+
   gdb_assert (loc != NULL);
 
   /* bp_call_dummy breakpoint locations are usually memory locations
@@ -9333,26 +9381,10 @@ bp_loc_is_permanent (struct bp_location *loc)
   if (loc->owner->type == bp_call_dummy)
     return 0;
 
-  addr = loc->address;
-  bpoint = gdbarch_breakpoint_from_pc (loc->gdbarch, &addr, &len);
-
-  /* Software breakpoints unsupported?  */
-  if (bpoint == NULL)
-    return 0;
-
-  target_mem = alloca (len);
-
-  /* Enable the automatic memory restoration from breakpoints while
-     we read the memory.  Otherwise we could say about our temporary
-     breakpoints they are permanent.  */
   cleanup = save_current_space_and_thread ();
-
   switch_to_program_space_and_thread (loc->pspace);
-  make_show_memory_breakpoints_cleanup (0);
 
-  if (target_read_memory (loc->address, target_mem, len) == 0
-      && memcmp (target_mem, bpoint, len) == 0)
-    retval = 1;
+  retval = program_breakpoint_here_p (loc->gdbarch, loc->address);
 
   do_cleanups (cleanup);
 
@@ -12797,8 +12829,7 @@ update_global_location_list (enum ugll_insert_mode insert_mode)
       if (!found_object)
 	{
 	  if (removed && non_stop
-	      && breakpoint_address_is_meaningful (old_loc->owner)
-	      && !is_hardware_watchpoint (old_loc->owner))
+	      && need_moribund_for_location_type (old_loc))
 	    {
 	      /* This location was removed from the target.  In
 		 non-stop mode, a race condition is possible where
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index ad91102..85c2240 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1117,6 +1117,11 @@ enum breakpoint_here
 
 /* Prototypes for breakpoint-related functions.  */
 
+/* Return 1 if there's a program/permanent breakpoint planted in
+   memory at ADDRESS, return 0 otherwise.  */
+
+extern int program_breakpoint_here_p (struct gdbarch *gdbarch, CORE_ADDR address);
+
 extern enum breakpoint_here breakpoint_here_p (struct address_space *, 
 					       CORE_ADDR);
 
diff --git a/gdb/infrun.c b/gdb/infrun.c
index abfeeee..8d3a9bf 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3468,6 +3468,18 @@ adjust_pc_after_break (struct execution_control_state *ecs)
   if (execution_direction == EXEC_REVERSE)
     return;
 
+  /* If the target can tell whether the thread hit a SW breakpoint,
+     trust it.  Targets that can tell also adjust the PC
+     themselves.  */
+  if (target_supports_stopped_by_sw_breakpoint ())
+    return;
+
+  /* Note that relying on whether a breakpoint is planted in memory to
+     determine this can fail.  E.g,. the breakpoint could have been
+     removed since.  Or the thread could have been told to step an
+     instruction the size of a breakpoint instruction, and only
+     _after_ was a breakpoint inserted at its address.  */
+
   /* If this target does not decrement the PC after breakpoints, then
      we have nothing to do.  */
   regcache = get_thread_regcache (ecs->ptid);
@@ -3483,6 +3495,11 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      breakpoint would be.  */
   breakpoint_pc = regcache_read_pc (regcache) - decr_pc;
 
+  /* If the target can't tell whether a software breakpoint triggered,
+     fallback to figuring it out based on breakpoints we think were
+     inserted in the target, and on whether the thread was stepped or
+     continued.  */
+
   /* Check whether there actually is a software breakpoint inserted at
      that location.
 
@@ -3490,7 +3507,10 @@ adjust_pc_after_break (struct execution_control_state *ecs)
      removed a breakpoint, but stop events for that breakpoint were
      already queued and arrive later.  To suppress those spurious
      SIGTRAPs, we keep a list of such breakpoint locations for a bit,
-     and retire them after a number of stop events are reported.  */
+     and retire them after a number of stop events are reported.  Note
+     this is an heuristic and can thus get confused.  The real fix is
+     to get the "stopped by SW BP and needs adjustment" info out of
+     the target/kernel (and thus never reach here; see above).  */
   if (software_breakpoint_inserted_here_p (aspace, breakpoint_pc)
       || (non_stop && moribund_breakpoint_here_p (aspace, breakpoint_pc)))
     {
@@ -4505,6 +4525,54 @@ handle_signal_stop (struct execution_control_state *ecs)
     = !bpstat_explains_signal (ecs->event_thread->control.stop_bpstat,
 			       ecs->event_thread->suspend.stop_signal);
 
+  /* Maybe this was a trap for a software breakpoint that has since
+     been removed.  */
+  if (random_signal && target_stopped_by_sw_breakpoint ())
+    {
+      if (program_breakpoint_here_p (gdbarch, stop_pc))
+	{
+	  struct regcache *regcache;
+	  int decr_pc;
+
+	  /* Re-adjust PC to what the program would see if GDB was not
+	     debugging it.  */
+	  regcache = get_thread_regcache (ecs->event_thread->ptid);
+	  decr_pc = target_decr_pc_after_break (gdbarch);
+	  if (decr_pc != 0)
+	    {
+	      struct cleanup *old_cleanups = make_cleanup (null_cleanup, NULL);
+
+	      if (record_full_is_used ())
+		record_full_gdb_operation_disable_set ();
+
+	      regcache_write_pc (regcache, stop_pc + decr_pc);
+
+	      do_cleanups (old_cleanups);
+	    }
+	}
+      else
+	{
+	  /* A delayed software breakpoint event.  Ignore the trap.  */
+	  if (debug_infrun)
+	    fprintf_unfiltered (gdb_stdlog,
+				"infrun: delayed software breakpoint "
+				"trap, ignoring\n");
+	  random_signal = 0;
+	}
+    }
+
+  /* Maybe this was a trap for a hardware breakpoint/watchpoint that
+     has since been removed.  */
+  if (random_signal && target_stopped_by_hw_breakpoint ())
+    {
+      /* A delayed hardware breakpoint event.  Ignore the trap.  */
+      if (debug_infrun)
+	fprintf_unfiltered (gdb_stdlog,
+			    "infrun: delayed hardware breakpoint/watchpoint "
+			    "trap, ignoring\n");
+      random_signal = 0;
+    }
+
   /* If not, perhaps stepping/nexting can.  */
   if (random_signal)
     random_signal = !(ecs->event_thread->suspend.stop_signal == GDB_SIGNAL_TRAP
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index e026179..1f827f5 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -293,6 +293,114 @@ debug_remove_breakpoint (struct target_ops *self, struct gdbarch *arg1, struct b
 }
 
 static int
+delegate_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  self = self->beneath;
+  return self->to_stopped_by_sw_breakpoint (self);
+}
+
+static int
+tdefault_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  return 0;
+}
+
+static int
+debug_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  int result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_stopped_by_sw_breakpoint (...)\n", debug_target.to_shortname);
+  result = debug_target.to_stopped_by_sw_breakpoint (&debug_target);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_stopped_by_sw_breakpoint (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_int (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+static int
+delegate_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  self = self->beneath;
+  return self->to_supports_stopped_by_sw_breakpoint (self);
+}
+
+static int
+tdefault_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  return 0;
+}
+
+static int
+debug_supports_stopped_by_sw_breakpoint (struct target_ops *self)
+{
+  int result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_supports_stopped_by_sw_breakpoint (...)\n", debug_target.to_shortname);
+  result = debug_target.to_supports_stopped_by_sw_breakpoint (&debug_target);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_supports_stopped_by_sw_breakpoint (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_int (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+static int
+delegate_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  self = self->beneath;
+  return self->to_stopped_by_hw_breakpoint (self);
+}
+
+static int
+tdefault_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  return 0;
+}
+
+static int
+debug_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  int result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_stopped_by_hw_breakpoint (...)\n", debug_target.to_shortname);
+  result = debug_target.to_stopped_by_hw_breakpoint (&debug_target);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_stopped_by_hw_breakpoint (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_int (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+static int
+delegate_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  self = self->beneath;
+  return self->to_supports_stopped_by_hw_breakpoint (self);
+}
+
+static int
+tdefault_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  return 0;
+}
+
+static int
+debug_supports_stopped_by_hw_breakpoint (struct target_ops *self)
+{
+  int result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->to_supports_stopped_by_hw_breakpoint (...)\n", debug_target.to_shortname);
+  result = debug_target.to_supports_stopped_by_hw_breakpoint (&debug_target);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->to_supports_stopped_by_hw_breakpoint (", debug_target.to_shortname);
+  target_debug_print_struct_target_ops_p (&debug_target);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_int (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+static int
 delegate_can_use_hw_breakpoint (struct target_ops *self, int arg1, int arg2, int arg3)
 {
   self = self->beneath;
@@ -3789,6 +3897,14 @@ install_delegators (struct target_ops *ops)
     ops->to_insert_breakpoint = delegate_insert_breakpoint;
   if (ops->to_remove_breakpoint == NULL)
     ops->to_remove_breakpoint = delegate_remove_breakpoint;
+  if (ops->to_stopped_by_sw_breakpoint == NULL)
+    ops->to_stopped_by_sw_breakpoint = delegate_stopped_by_sw_breakpoint;
+  if (ops->to_supports_stopped_by_sw_breakpoint == NULL)
+    ops->to_supports_stopped_by_sw_breakpoint = delegate_supports_stopped_by_sw_breakpoint;
+  if (ops->to_stopped_by_hw_breakpoint == NULL)
+    ops->to_stopped_by_hw_breakpoint = delegate_stopped_by_hw_breakpoint;
+  if (ops->to_supports_stopped_by_hw_breakpoint == NULL)
+    ops->to_supports_stopped_by_hw_breakpoint = delegate_supports_stopped_by_hw_breakpoint;
   if (ops->to_can_use_hw_breakpoint == NULL)
     ops->to_can_use_hw_breakpoint = delegate_can_use_hw_breakpoint;
   if (ops->to_ranged_break_num_registers == NULL)
@@ -4061,6 +4177,10 @@ install_dummy_methods (struct target_ops *ops)
   ops->to_files_info = tdefault_files_info;
   ops->to_insert_breakpoint = memory_insert_breakpoint;
   ops->to_remove_breakpoint = memory_remove_breakpoint;
+  ops->to_stopped_by_sw_breakpoint = tdefault_stopped_by_sw_breakpoint;
+  ops->to_supports_stopped_by_sw_breakpoint = tdefault_supports_stopped_by_sw_breakpoint;
+  ops->to_stopped_by_hw_breakpoint = tdefault_stopped_by_hw_breakpoint;
+  ops->to_supports_stopped_by_hw_breakpoint = tdefault_supports_stopped_by_hw_breakpoint;
   ops->to_can_use_hw_breakpoint = tdefault_can_use_hw_breakpoint;
   ops->to_ranged_break_num_registers = tdefault_ranged_break_num_registers;
   ops->to_insert_hw_breakpoint = tdefault_insert_hw_breakpoint;
@@ -4205,6 +4325,10 @@ init_debug_target (struct target_ops *ops)
   ops->to_files_info = debug_files_info;
   ops->to_insert_breakpoint = debug_insert_breakpoint;
   ops->to_remove_breakpoint = debug_remove_breakpoint;
+  ops->to_stopped_by_sw_breakpoint = debug_stopped_by_sw_breakpoint;
+  ops->to_supports_stopped_by_sw_breakpoint = debug_supports_stopped_by_sw_breakpoint;
+  ops->to_stopped_by_hw_breakpoint = debug_stopped_by_hw_breakpoint;
+  ops->to_supports_stopped_by_hw_breakpoint = debug_supports_stopped_by_hw_breakpoint;
   ops->to_can_use_hw_breakpoint = debug_can_use_hw_breakpoint;
   ops->to_ranged_break_num_registers = debug_ranged_break_num_registers;
   ops->to_insert_hw_breakpoint = debug_insert_hw_breakpoint;
diff --git a/gdb/target.h b/gdb/target.h
index 5ba8425..a7c2e82 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -454,6 +454,35 @@ struct target_ops
     int (*to_remove_breakpoint) (struct target_ops *, struct gdbarch *,
 				 struct bp_target_info *)
       TARGET_DEFAULT_FUNC (memory_remove_breakpoint);
+
+    /* Returns true if the target stopped because it executed a
+       software breakpoint.  This is necessary for correct background
+       execution / non-stop mode operation, and for correct PC
+       adjustment on targets where the PC needs to be adjusted when a
+       software breakpoint triggers.  In these modes, by the time GDB
+       processes a breakpoint event, the breakpoint may already be
+       done from the target, so GDB needs to be able to tell whether
+       it should ignore the event and whether it should adjust the PC.
+       See adjust_pc_after_break.  */
+    int (*to_stopped_by_sw_breakpoint) (struct target_ops *)
+      TARGET_DEFAULT_RETURN (0);
+    /* Returns true if the above method is supported.  */
+    int (*to_supports_stopped_by_sw_breakpoint) (struct target_ops *)
+      TARGET_DEFAULT_RETURN (0);
+
+    /* Returns true if the target stopped for a hardware breakpoint.
+       Likewise, if the target supports hardware breakpoints, this
+       method is necessary for correct background execution / non-stop
+       mode operation.  Even though hardware breakpoints do not
+       require PC adjustment, GDB needs to be able to tell whether the
+       hardware breakpoint event is a delayed event for a breakpoint
+       that is already gone and should thus be ignored.  */
+    int (*to_stopped_by_hw_breakpoint) (struct target_ops *)
+      TARGET_DEFAULT_RETURN (0);
+    /* Returns true if the above method is supported.  */
+    int (*to_supports_stopped_by_hw_breakpoint) (struct target_ops *)
+      TARGET_DEFAULT_RETURN (0);
+
     int (*to_can_use_hw_breakpoint) (struct target_ops *, int, int, int)
       TARGET_DEFAULT_RETURN (0);
     int (*to_ranged_break_num_registers) (struct target_ops *)
@@ -1743,6 +1772,21 @@ extern char *target_thread_name (struct thread_info *);
 #define target_stopped_by_watchpoint()		\
   ((*current_target.to_stopped_by_watchpoint) (&current_target))
 
+/* Returns non-zero if the target stopped because it executed a
+   software breakpoint instruction.  */
+
+#define target_stopped_by_sw_breakpoint()		\
+  ((*current_target.to_stopped_by_sw_breakpoint) (&current_target))
+
+#define target_supports_stopped_by_sw_breakpoint() \
+  ((*current_target.to_supports_stopped_by_sw_breakpoint) (&current_target))
+
+#define target_stopped_by_hw_breakpoint()				\
+  ((*current_target.to_stopped_by_hw_breakpoint) (&current_target))
+
+#define target_supports_stopped_by_hw_breakpoint() \
+  ((*current_target.to_supports_stopped_by_hw_breakpoint) (&current_target))
+
 /* Non-zero if we have steppable watchpoints  */
 
 #define target_have_steppable_watchpoint \


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]