Put single-step breakpoints on the bp_location chain
authorPedro Alves <palves@redhat.com>
Wed, 15 Oct 2014 19:18:31 +0000 (20:18 +0100)
committerPedro Alves <palves@redhat.com>
Wed, 15 Oct 2014 19:18:31 +0000 (20:18 +0100)
This patch makes single-step breakpoints "real" breakpoints on the
global location list.

There are several benefits to this:

- It removes the currently limitation that only 2 single-step
  breakpoints can be inserted.  See an example here of a discussion
  around a case that wants more than 2, possibly unbounded:

  https://sourceware.org/ml/gdb-patches/2014-03/msg00663.html

- makes software single-step work on read-only code regions.

  The logic to convert a software breakpoint to a hardware breakpoint
  if the memory map says the breakpoint address is in read only memory
  is in insert_bp_location.  Because software single-step breakpoints
  bypass all that go and straight to target_insert_breakpoint, we
  can't software single-step over read only memory.  This patch
  removes that limitation, and adds a test that makes sure that works,
  by forcing a code region to read-only with "mem LOW HIGH ro" and
  then stepping through that.

- Fixes PR breakpoints/9649

  This is an assertion failure in insert_single_step_breakpoint in
  breakpoint.c, because we may leave stale single-step breakpoints
  behind on error.

  The tests for stepping through read-only regions exercise the root
  cause of the bug, which is that we leave single-step breakpoints
  behind if we fail to insert any single-step breakpoint.  Deleting
  the single-step breakpoints in resume_cleanups,
  delete_just_stopped_threads_infrun_breakpoints, and
  fetch_inferior_event fixes this.  Without that, we'd no longer hit
  the assertion, as that code is deleted, but we'd instead run into
  errors/warnings trying to insert/remove the stale breakpoints on
  next resume.

- Paves the way to have multiple threads software single-stepping at
  the same time, leaving update_global_location_list to worry about
  duplicate locations.

- Makes the moribund location machinery aware of software single-step
  breakpoints, paving the way to enable software single-step on
  non-stop, instead of forcing serialized displaced stepping for all
  single steps.

- It's generaly cleaner.

  We no longer have to play games with single-step breakpoints
  inserted at the same address as regular breakpoints, like we
  recently had to do for 7.8.  See this discussion:

  https://sourceware.org/ml/gdb-patches/2014-06/msg00052.html.

Tested on x86_64 Fedora 20, on top of my 'single-step breakpoints on
x86' series.

gdb/
2014-10-15  Pedro Alves  <palves@redhat.com>

PR breakpoints/9649
* breakpoint.c (single_step_breakpoints, single_step_gdbarch):
Delete array globals.
(single_step_breakpoints): New global.
(breakpoint_xfer_memory): Remove special handling for single-step
breakpoints.
(update_breakpoints_after_exec): Delete bp_single_step
breakpoints.
(detach_breakpoints): Remove special handling for single-step
breakpoints.
(breakpoint_init_inferior): Delete bp_single_step breakpoints.
(bpstat_stop_status): Add comment.
(bpstat_what, bptype_string, print_one_breakpoint_location)
(adjust_breakpoint_address, init_bp_location): Handle
bp_single_step.
(new_single_step_breakpoint): New function.
(set_momentary_breakpoint, bkpt_remove_location): Remove special
handling for single-step breakpoints.
(insert_single_step_breakpoint, single_step_breakpoints_inserted)
(remove_single_step_breakpoints, cancel_single_step_breakpoints):
Rewrite.
(detach_single_step_breakpoints, find_single_step_breakpoint):
Delete functions.
(breakpoint_has_location_inserted_here): New function.
(single_step_breakpoint_inserted_here_p): Rewrite.
* breakpoint.h: Remove FIXME.
(enum bptype) <bp_single_step>: New enum value.
(insert_single_step_breakpoint): Update comment.
* infrun.c (resume_cleanups)
(delete_step_thread_step_resume_breakpoint): Remove single-step
breakpoints.
(fetch_inferior_event): Install a cleanup that removes infrun
breakpoints.
(switch_back_to_stepped_thread) <expect thread advanced also>:
Clear step-over info.

gdb/testsuite/
2014-10-15  Pedro Alves  <palves@redhat.com>

PR breakpoints/9649
* gdb.base/breakpoint-in-ro-region.c (main): Add more instructions.
* gdb.base/breakpoint-in-ro-region.exp
(probe_target_hardware_step): New procedure.
(top level): Probe hardware stepping and hardware breakpoint
support.  Test stepping through a read-only region, with both
"breakpoint auto-hw" on and off and both "always-inserted" on and
off.

gdb/ChangeLog
gdb/breakpoint.c
gdb/breakpoint.h
gdb/infrun.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/breakpoint-in-ro-region.c
gdb/testsuite/gdb.base/breakpoint-in-ro-region.exp

index ef803bbca42e1d0f520141fd7b12ce90163716c0..1affa2c0926767842fbebf3265482bd23d8c6d78 100644 (file)
@@ -1,3 +1,41 @@
+2014-10-15  Pedro Alves  <palves@redhat.com>
+
+       PR breakpoints/9649
+       * breakpoint.c (single_step_breakpoints, single_step_gdbarch):
+       Delete array globals.
+       (single_step_breakpoints): New global.
+       (breakpoint_xfer_memory): Remove special handling for single-step
+       breakpoints.
+       (update_breakpoints_after_exec): Delete bp_single_step
+       breakpoints.
+       (detach_breakpoints): Remove special handling for single-step
+       breakpoints.
+       (breakpoint_init_inferior): Delete bp_single_step breakpoints.
+       (bpstat_stop_status): Add comment.
+       (bpstat_what, bptype_string, print_one_breakpoint_location)
+       (adjust_breakpoint_address, init_bp_location): Handle
+       bp_single_step.
+       (new_single_step_breakpoint): New function.
+       (set_momentary_breakpoint, bkpt_remove_location): Remove special
+       handling for single-step breakpoints.
+       (insert_single_step_breakpoint, single_step_breakpoints_inserted)
+       (remove_single_step_breakpoints, cancel_single_step_breakpoints):
+       Rewrite.
+       (detach_single_step_breakpoints, find_single_step_breakpoint):
+       Delete functions.
+       (breakpoint_has_location_inserted_here): New function.
+       (single_step_breakpoint_inserted_here_p): Rewrite.
+       * breakpoint.h: Remove FIXME.
+       (enum bptype) <bp_single_step>: New enum value.
+       (insert_single_step_breakpoint): Update comment.
+       * infrun.c (resume_cleanups)
+       (delete_step_thread_step_resume_breakpoint): Remove single-step
+       breakpoints.
+       (fetch_inferior_event): Install a cleanup that removes infrun
+       breakpoints.
+       (switch_back_to_stepped_thread) <expect thread advanced also>:
+       Clear step-over info.
+
 2014-10-15  Pedro Alves  <palves@redhat.com>
 
        * infrun.c (delete_step_resume_breakpoint_callback): Delete.
index 2a6e51d9713b2af694e97d36a44c22e38ba871f9..27420f5fb13d390df883c33b047c92a348098e02 100644 (file)
@@ -225,11 +225,6 @@ static void stopat_command (char *arg, int from_tty);
 
 static void tcatch_command (char *arg, int from_tty);
 
-static void detach_single_step_breakpoints (void);
-
-static int find_single_step_breakpoint (struct address_space *aspace,
-                                       CORE_ADDR pc);
-
 static void free_bp_location (struct bp_location *loc);
 static void incref_bp_location (struct bp_location *loc);
 static void decref_bp_location (struct bp_location **loc);
@@ -333,8 +328,7 @@ struct breakpoint_ops dprintf_breakpoint_ops;
 /* One (or perhaps two) breakpoints used for software single
    stepping.  */
 
-static void *single_step_breakpoints[2];
-static struct gdbarch *single_step_gdbarch[2];
+static struct breakpoint *single_step_breakpoints;
 
 /* The style in which to perform a dynamic printf.  This is a user
    option because different output options have different tradeoffs;
@@ -1664,21 +1658,6 @@ breakpoint_xfer_memory (gdb_byte *readbuf, gdb_byte *writebuf,
     one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org,
                                memaddr, len, &bl->target_info, bl->gdbarch);
   }
-
-  /* Now process single-step breakpoints.  These are not found in the
-     bp_location array.  */
-  for (i = 0; i < 2; i++)
-    {
-      struct bp_target_info *bp_tgt = single_step_breakpoints[i];
-
-      if (bp_tgt != NULL)
-       {
-         struct gdbarch *gdbarch = single_step_gdbarch[i];
-
-         one_breakpoint_xfer_memory (readbuf, writebuf, writebuf_org,
-                                     memaddr, len, bp_tgt, gdbarch);
-       }
-    }
 }
 
 \f
@@ -3792,6 +3771,13 @@ update_breakpoints_after_exec (void)
        continue;
       }
 
+    /* Just like single-step breakpoints.  */
+    if (b->type == bp_single_step)
+      {
+       delete_breakpoint (b);
+       continue;
+      }
+
     /* Longjmp and longjmp-resume breakpoints are also meaningless
        after an exec.  */
     if (b->type == bp_longjmp || b->type == bp_longjmp_resume
@@ -3884,9 +3870,6 @@ detach_breakpoints (ptid_t ptid)
       val |= remove_breakpoint_1 (bl, mark_inserted);
   }
 
-  /* Detach single-step breakpoints as well.  */
-  detach_single_step_breakpoints ();
-
   do_cleanups (old_chain);
   return val;
 }
@@ -4156,6 +4139,10 @@ breakpoint_init_inferior (enum inf_context context)
 
        /* Also remove step-resume breakpoints.  */
 
+      case bp_single_step:
+
+       /* Also remove single-step breakpoints.  */
+
        delete_breakpoint (b);
        break;
 
@@ -5632,6 +5619,7 @@ 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 (breakpoint_location_address_match (loc, aspace, bp_addr))
@@ -5787,6 +5775,7 @@ bpstat_what (bpstat bs_head)
          break;
        case bp_breakpoint:
        case bp_hardware_breakpoint:
+       case bp_single_step:
        case bp_until:
        case bp_finish:
        case bp_shlib_event:
@@ -6145,6 +6134,7 @@ bptype_string (enum bptype type)
     {bp_none, "?deleted?"},
     {bp_breakpoint, "breakpoint"},
     {bp_hardware_breakpoint, "hw breakpoint"},
+    {bp_single_step, "sw single-step"},
     {bp_until, "until"},
     {bp_finish, "finish"},
     {bp_watchpoint, "watchpoint"},
@@ -6336,6 +6326,7 @@ print_one_breakpoint_location (struct breakpoint *b,
 
       case bp_breakpoint:
       case bp_hardware_breakpoint:
+      case bp_single_step:
       case bp_until:
       case bp_finish:
       case bp_longjmp:
@@ -7178,6 +7169,16 @@ adjust_breakpoint_address (struct gdbarch *gdbarch,
          have their addresses modified.  */
       return bpaddr;
     }
+  else if (bptype == bp_single_step)
+    {
+      /* Single-step breakpoints should not have their addresses
+        modified.  If there's any architectural constrain that
+        applies to this address, then it should have already been
+        taken into account when the breakpoint was created in the
+        first place.  If we didn't do this, stepping through e.g.,
+        Thumb-2 IT blocks would break.  */
+      return bpaddr;
+    }
   else
     {
       CORE_ADDR adjusted_bpaddr;
@@ -7214,6 +7215,7 @@ init_bp_location (struct bp_location *loc, const struct bp_location_ops *ops,
   switch (owner->type)
     {
     case bp_breakpoint:
+    case bp_single_step:
     case bp_until:
     case bp_finish:
     case bp_longjmp:
@@ -9196,10 +9198,31 @@ enable_breakpoints_after_startup (void)
   breakpoint_re_set ();
 }
 
+/* Create a new single-step breakpoint for thread THREAD, with no
+   locations.  */
 
-/* Set a breakpoint that will evaporate an end of command
-   at address specified by SAL.
-   Restrict it to frame FRAME if FRAME is nonzero.  */
+static struct breakpoint *
+new_single_step_breakpoint (int thread, struct gdbarch *gdbarch)
+{
+  struct breakpoint *b = XNEW (struct breakpoint);
+
+  init_raw_breakpoint_without_location (b, gdbarch, bp_single_step,
+                                       &momentary_breakpoint_ops);
+
+  b->disposition = disp_donttouch;
+  b->frame_id = null_frame_id;
+
+  b->thread = thread;
+  gdb_assert (b->thread != 0);
+
+  add_to_breakpoint_chain (b);
+
+  return b;
+}
+
+/* Set a momentary breakpoint of type TYPE at address specified by
+   SAL.  If FRAME_ID is valid, the breakpoint is restricted to that
+   frame.  */
 
 struct breakpoint *
 set_momentary_breakpoint (struct gdbarch *gdbarch, struct symtab_and_line sal,
@@ -13324,28 +13347,9 @@ static int
 bkpt_insert_location (struct bp_location *bl)
 {
   if (bl->loc_type == bp_loc_hardware_breakpoint)
-    return target_insert_hw_breakpoint (bl->gdbarch,
-                                       &bl->target_info);
+    return target_insert_hw_breakpoint (bl->gdbarch, &bl->target_info);
   else
-    {
-      struct bp_target_info *bp_tgt = &bl->target_info;
-      int ret;
-      int sss_slot;
-
-      /* There is no need to insert a breakpoint if an unconditional
-        raw/sss breakpoint is already inserted at that location.  */
-      sss_slot = find_single_step_breakpoint (bp_tgt->placed_address_space,
-                                             bp_tgt->reqstd_address);
-      if (sss_slot >= 0)
-       {
-         struct bp_target_info *sss_bp_tgt = single_step_breakpoints[sss_slot];
-
-         bp_target_info_copy_insertion_state (bp_tgt, sss_bp_tgt);
-         return 0;
-       }
-
-      return target_insert_breakpoint (bl->gdbarch, bp_tgt);
-    }
+    return target_insert_breakpoint (bl->gdbarch, &bl->target_info);
 }
 
 static int
@@ -13354,19 +13358,7 @@ bkpt_remove_location (struct bp_location *bl)
   if (bl->loc_type == bp_loc_hardware_breakpoint)
     return target_remove_hw_breakpoint (bl->gdbarch, &bl->target_info);
   else
-    {
-      struct bp_target_info *bp_tgt = &bl->target_info;
-      struct address_space *aspace = bp_tgt->placed_address_space;
-      CORE_ADDR address = bp_tgt->reqstd_address;
-
-      /* Only remove the breakpoint if there is no raw/sss breakpoint
-        still inserted at this location.  Otherwise, we would be
-        effectively disabling the raw/sss breakpoint.  */
-      if (single_step_breakpoint_inserted_here_p (aspace, address))
-       return 0;
-
-      return target_remove_breakpoint (bl->gdbarch, bp_tgt);
-    }
+    return target_remove_breakpoint (bl->gdbarch, &bl->target_info);
 }
 
 static int
@@ -15454,31 +15446,20 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch,
                               struct address_space *aspace, 
                               CORE_ADDR next_pc)
 {
-  void **bpt_p;
+  struct thread_info *tp = inferior_thread ();
+  struct symtab_and_line sal;
+  CORE_ADDR pc = next_pc;
 
-  if (single_step_breakpoints[0] == NULL)
-    {
-      bpt_p = &single_step_breakpoints[0];
-      single_step_gdbarch[0] = gdbarch;
-    }
-  else
-    {
-      gdb_assert (single_step_breakpoints[1] == NULL);
-      bpt_p = &single_step_breakpoints[1];
-      single_step_gdbarch[1] = gdbarch;
-    }
+  if (single_step_breakpoints == NULL)
+    single_step_breakpoints = new_single_step_breakpoint (tp->num, gdbarch);
 
-  /* NOTE drow/2006-04-11: A future improvement to this function would
-     be to only create the breakpoints once, and actually put them on
-     the breakpoint chain.  That would let us use set_raw_breakpoint.
-     We could adjust the addresses each time they were needed.  Doing
-     this requires corresponding changes elsewhere where single step
-     breakpoints are handled, however.  So, for now, we use this.  */
+  sal = find_pc_line (pc, 0);
+  sal.pc = pc;
+  sal.section = find_pc_overlay (pc);
+  sal.explicit_pc = 1;
+  add_location_to_breakpoint (single_step_breakpoints, &sal);
 
-  *bpt_p = deprecated_insert_raw_breakpoint (gdbarch, aspace, next_pc);
-  if (*bpt_p == NULL)
-    error (_("Could not insert single-step breakpoint at %s"),
-            paddress (gdbarch, next_pc));
+  update_global_location_list (UGLL_INSERT);
 }
 
 /* Check if the breakpoints used for software single stepping
@@ -15487,8 +15468,7 @@ insert_single_step_breakpoint (struct gdbarch *gdbarch,
 int
 single_step_breakpoints_inserted (void)
 {
-  return (single_step_breakpoints[0] != NULL
-          || single_step_breakpoints[1] != NULL);
+  return (single_step_breakpoints != NULL);
 }
 
 /* Remove and delete any breakpoints used for software single step.  */
@@ -15496,22 +15476,11 @@ single_step_breakpoints_inserted (void)
 void
 remove_single_step_breakpoints (void)
 {
-  gdb_assert (single_step_breakpoints[0] != NULL);
+  gdb_assert (single_step_breakpoints != NULL);
 
-  /* See insert_single_step_breakpoint for more about this deprecated
-     call.  */
-  deprecated_remove_raw_breakpoint (single_step_gdbarch[0],
-                                   single_step_breakpoints[0]);
-  single_step_gdbarch[0] = NULL;
-  single_step_breakpoints[0] = NULL;
+  delete_breakpoint (single_step_breakpoints);
 
-  if (single_step_breakpoints[1] != NULL)
-    {
-      deprecated_remove_raw_breakpoint (single_step_gdbarch[1],
-                                       single_step_breakpoints[1]);
-      single_step_gdbarch[1] = NULL;
-      single_step_breakpoints[1] = NULL;
-    }
+  single_step_breakpoints = NULL;
 }
 
 /* Delete software single step breakpoints without removing them from
@@ -15522,51 +15491,28 @@ remove_single_step_breakpoints (void)
 void
 cancel_single_step_breakpoints (void)
 {
-  int i;
-
-  for (i = 0; i < 2; i++)
-    if (single_step_breakpoints[i])
-      {
-       xfree (single_step_breakpoints[i]);
-       single_step_breakpoints[i] = NULL;
-       single_step_gdbarch[i] = NULL;
-      }
-}
-
-/* Detach software single-step breakpoints from INFERIOR_PTID without
-   removing them.  */
-
-static void
-detach_single_step_breakpoints (void)
-{
-  int i;
-
-  for (i = 0; i < 2; i++)
-    if (single_step_breakpoints[i])
-      target_remove_breakpoint (single_step_gdbarch[i],
-                               single_step_breakpoints[i]);
+  /* We don't really need to (or should) delete them here.  After an
+     exit, breakpoint_init_inferior deletes it.  After an exec,
+     update_breakpoints_after_exec does it.  Just clear our
+     reference.  */
+  single_step_breakpoints = NULL;
 }
 
-/* Find the software single-step breakpoint that inserted at PC.
-   Returns its slot if found, and -1 if not found.  */
+/* Check whether any location of BP is inserted at PC.  */
 
 static int
-find_single_step_breakpoint (struct address_space *aspace,
-                            CORE_ADDR pc)
+breakpoint_has_location_inserted_here (struct breakpoint *bp,
+                                      struct address_space *aspace,
+                                      CORE_ADDR pc)
 {
-  int i;
+  struct bp_location *loc;
 
-  for (i = 0; i < 2; i++)
-    {
-      struct bp_target_info *bp_tgt = single_step_breakpoints[i];
-      if (bp_tgt
-         && breakpoint_address_match (bp_tgt->placed_address_space,
-                                      bp_tgt->reqstd_address,
-                                      aspace, pc))
-       return i;
-    }
+  for (loc = bp->loc; loc != NULL; loc = loc->next)
+    if (loc->inserted
+       && breakpoint_location_address_match (loc, aspace, pc))
+      return 1;
 
-  return -1;
+  return 0;
 }
 
 /* Check whether a software single-step breakpoint is inserted at
@@ -15576,7 +15522,9 @@ int
 single_step_breakpoint_inserted_here_p (struct address_space *aspace,
                                        CORE_ADDR pc)
 {
-  return find_single_step_breakpoint (aspace, pc) >= 0;
+  return (single_step_breakpoints != NULL
+         && breakpoint_has_location_inserted_here (single_step_breakpoints,
+                                                   aspace, pc));
 }
 
 /* Returns 0 if 'bp' is NOT a syscall catchpoint,
index b611057a15d1883076530e66a54cfa562dc98196..7c563c10fd461b474ee195b5bf8f78055a0248b1 100644 (file)
@@ -47,18 +47,13 @@ struct linespec_sals;
 \f
 
 /* Type of breakpoint.  */
-/* FIXME In the future, we should fold all other breakpoint-like
-   things into here.  This includes:
-
-   * single-step (for machines where we have to simulate single
-   stepping) (probably, though perhaps it is better for it to look as
-   much as possible like a single-step to wait_for_inferior).  */
 
 enum bptype
   {
     bp_none = 0,               /* Eventpoint has been deleted */
     bp_breakpoint,             /* Normal breakpoint */
     bp_hardware_breakpoint,    /* Hardware assisted breakpoint */
+    bp_single_step,            /* Software single-step */
     bp_until,                  /* used by until command */
     bp_finish,                 /* used by finish command */
     bp_watchpoint,             /* Watchpoint */
@@ -1461,8 +1456,10 @@ extern void add_solib_catchpoint (char *arg, int is_load, int is_temp,
    deletes all breakpoints.  */
 extern void delete_command (char *arg, int from_tty);
 
-/* Manage a software single step breakpoint (or two).  Insert may be
-   called twice before remove is called.  */
+/* Create and insert a new software single step breakpoint for the
+   current thread.  May be called multiple times; each time will add a
+   new location to the set of potential addresses the next instruction
+   is at.  */
 extern void insert_single_step_breakpoint (struct gdbarch *,
                                           struct address_space *, 
                                           CORE_ADDR);
index aa36dbc843dca847a207bb53502673e2718cb5f6..29c37e1fefeb6acb4f2258237c29d8c5df32ffc6 100644 (file)
@@ -1918,6 +1918,9 @@ infrun_thread_ptid_changed (ptid_t old_ptid, ptid_t new_ptid)
 static void
 resume_cleanups (void *ignore)
 {
+  if (single_step_breakpoints_inserted ())
+    remove_single_step_breakpoints ();
+
   normal_stop ();
 }
 
@@ -2893,6 +2896,9 @@ static void
 delete_just_stopped_threads_infrun_breakpoints (void)
 {
   for_each_just_stopped_thread (delete_thread_infrun_breakpoints);
+
+  if (single_step_breakpoints_inserted ())
+    remove_single_step_breakpoints ();
 }
 
 /* A cleanup wrapper.  */
@@ -3152,6 +3158,8 @@ fetch_inferior_event (void *client_data)
      still for the thread which has thrown the exception.  */
   make_bpstat_clear_actions_cleanup ();
 
+  make_cleanup (delete_just_stopped_threads_infrun_breakpoints_cleanup, NULL);
+
   /* Now figure out what to do with the result of the result.  */
   handle_inferior_event (ecs);
 
@@ -5525,6 +5533,14 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
                fprintf_unfiltered (gdb_stdlog,
                                    "infrun: expected thread advanced also\n");
 
+             /* Clear the info of the previous step-over, as it's no
+                longer valid.  It's what keep_going would do too, if
+                we called it.  Must do this before trying to insert
+                the sss breakpoint, otherwise if we were previously
+                trying to step over this exact address in another
+                thread, the breakpoint ends up not installed.  */
+             clear_step_over_info ();
+
              insert_single_step_breakpoint (get_frame_arch (frame),
                                             get_frame_address_space (frame),
                                             stop_pc);
index 800a483dc6e03ded9a0b58d7d4091e3de09faa96..52640875be8ae965d2bb1ba763a250b1f2f4b945 100644 (file)
@@ -1,3 +1,14 @@
+2014-10-15  Pedro Alves  <palves@redhat.com>
+
+       PR breakpoints/9649
+       * gdb.base/breakpoint-in-ro-region.c (main): Add more instructions.
+       * gdb.base/breakpoint-in-ro-region.exp
+       (probe_target_hardware_step): New procedure.
+       (top level): Probe hardware stepping and hardware breakpoint
+       support.  Test stepping through a read-only region, with both
+       "breakpoint auto-hw" on and off and both "always-inserted" on and
+       off.
+
 2014-10-15  Iain Buclaw  <ibuclaw@gdcproject.org>
 
        * gdb.dlang/demangle.exp: Update for demangling changes.
index 696a67dae4bf08acd27e8b2235ad9edca0dd3293..2a999b18aeb0ed490dd7cacf8cdb4d79abd553cf 100644 (file)
@@ -23,6 +23,15 @@ main (void)
   i = 0;
   i = 0;
   i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
+  i = 0;
 
   return 0;
 }
index 8eacefa55733240421ac9f71e7a1b80e026d57e3..1991aa59099823aba4bc967264abcb0ee304e65b 100644 (file)
@@ -27,6 +27,28 @@ if ![runto main] {
 
 delete_breakpoints
 
+# Probe for hardware stepping.
+
+proc probe_target_hardware_step {} {
+    global gdb_prompt
+
+    set hw_step 0
+
+    gdb_test_no_output "set debug target 1"
+    set test "probe target hardware step"
+    gdb_test_multiple "si" $test {
+       -re "to_resume \\(\[^\r\n\]+, step, .*$gdb_prompt $" {
+           set hw_step 1
+           pass $test
+       }
+       -re "$gdb_prompt $" {
+           pass $test
+       }
+    }
+    gdb_test "set debug target 0" "->to_log_command.*\\).*"
+    return $hw_step
+}
+
 # Get the bounds of a function, and write them to FUNC_LO (inclusive),
 # FUNC_HI (exclusive).  Return true on success and false on failure.
 proc get_function_bounds {function func_lo func_hi} {
@@ -108,6 +130,7 @@ proc get_next_insn {} {
     return $next
 }
 
+set hw_step [probe_target_hardware_step]
 
 if ![get_function_bounds "main" main_lo main_hi] {
     # Can't do the following tests if main's bounds are unknown.
@@ -140,3 +163,84 @@ gdb_test "p /x *(char *) $main_lo = 1" \
 gdb_test "break *$main_lo" \
     "Cannot insert breakpoint .*Cannot set software breakpoint at read-only address $main_lo.*" \
     "inserting software breakpoint in read-only memory fails"
+
+delete_breakpoints
+
+set supports_hbreak 0
+set test "probe hbreak support"
+gdb_test_multiple "hbreak *$main_lo" $test {
+    -re "You may have requested too many.*$gdb_prompt $" {
+       pass "$test (no support)"
+    }
+    -re "No hardware breakpoint support.*$gdb_prompt $" {
+       pass "$test (no support)"
+    }
+    -re "$gdb_prompt $" {
+       pass "$test (support)"
+       set supports_hbreak 1
+    }
+}
+
+delete_breakpoints
+
+# Check that the "auto-hw on/off" setting affects single-step
+# breakpoints as expected, by stepping through the read-only region.
+# If the target does hardware stepping, we won't exercise that aspect,
+# but we should be able to step through the region without seeing the
+# hardware breakpoint or read-only address errors.
+proc test_single_step { always_inserted auto_hw } {
+    global gdb_prompt
+    global decimal
+    global supports_hbreak
+    global hw_step
+
+    gdb_test_no_output "set breakpoint always-inserted $always_inserted"
+    gdb_test_no_output "set breakpoint auto-hw $auto_hw"
+
+    # Get the address of the current instruction so we know where SI is
+    # starting from.
+    set curr_insn [get_curr_insn]
+
+    # Get the address of the next instruction so we know where SI should
+    # land.
+    set next_insn [get_next_insn]
+
+    set test "step in ro region"
+    gdb_test_multiple "si" $test {
+       -re "Could not insert hardware breakpoints.*$gdb_prompt $" {
+           gdb_assert {!$hw_step && $auto_hw == "on" && !$supports_hbreak} \
+               "$test (cannot insert hw break)"
+       }
+       -re "Cannot set software breakpoint at read-only address $next_insn.*$gdb_prompt $" {
+           gdb_assert {!$hw_step && $auto_hw == "off"} \
+               "$test (cannot insert sw break)"
+       }
+       -re "^si\r\nNote: automatically using hardware breakpoints for read-only addresses\.\r\n${decimal}\[ \t\]+i = 0;\r\n$gdb_prompt $" {
+           gdb_assert {!$hw_step && $auto_hw == "on" && $supports_hbreak} \
+               "$test (auto-hw)"
+       }
+       -re "^si\r\n${decimal}\[ \t\]+i = 0;\r\n$gdb_prompt $" {
+           gdb_assert {$hw_step || ($auto_hw == "on" && $supports_hbreak)} \
+               "$test (no error)"
+       }
+    }
+
+    gdb_test "maint info breakpoints 0" \
+       "No breakpoint or watchpoint matching '0'\." \
+       "single-step breakpoint is not left behind"
+
+    # Confirm the thread really advanced.
+    if {$hw_step || ($auto_hw == "on" && $supports_hbreak)} {
+       gdb_test "p /x \$pc" " = $next_insn" "thread advanced"
+    } else {
+       gdb_test "p /x \$pc" " = $curr_insn" "thread did not advance"
+    }
+}
+
+foreach always_inserted {"off" "on"} {
+    foreach auto_hw {"off" "on"} {
+       with_test_prefix "always-inserted $always_inserted: auto-hw $auto_hw" {
+           test_single_step $always_inserted $auto_hw
+       }
+    }
+}