[RFA] enable/disable sub breakpoint range

Message ID 7bbb10f0-41f4-6fef-f9d2-769e6032d445@adacore.com
State New, archived
Headers

Commit Message

Xavier Roirand Sept. 27, 2017, 2:49 p.m. UTC
  Add enable/disable sub breakpoint capability.

In some cases, adding one breakpoint corresponds to multiple
places in a program thus leads GDB to define main breakpoint
number and let's say "sub breakpoint" using location number
with syntax <breakpoint_number>.<location_number>.

For example:
Num  Type       Disp Enb  Address    What
1    breakpoint keep y    <MULTIPLE>
1.1                  y    0x080486a2 in void foo<int>()...
1.2                  y    0x080486ca in void foo<double>()...

In this example, main breakpoint is breakpoint 1 where sub
breakpoint are 1.1 and 1.2 ones.

This patch allows enable/disable a range of sub breakpoints
using syntax:

<breakpoint_number>.<first_location_number>-<last_location_number>

with inclusive last_location_number.

For instance, if adding a breakpoint to foo() generates 5 sub
breakpoints from 1.1 to 1.5 then it's now possible to enable/disable
only sub breakpoint 1.3 to sub breakpoint 1.5 (so 1.3, 1.4 and 1.5)
using syntax:

enable 1.3-5 or disable 1.3-5

gdb/ChangeLog:

* breakpoint.c (map_breakpoint_number): Create from
map_breakpoint_numbers refactoring.
(map_breakpoint_numbers): Refactor by calling
map_breakpoint_number.
(find_location_by_number): Change parameters from string to
breakpoint number and location.
(extract_bp_number_and_location): New function.
(enable_disable_bp_num_loc): Create from enable/disable_command
refactoring.
(enable_disable_command): Create from enable/disable_command
refactoring.
(enable_command): Refactor using enable_disable_command.
(disable_command): Refactor using enable_disable_command.

gdb/doc/ChangeLog:

* gdb.texinfo (Set Breaks): Add documentation for sub breakpoint
   range enable/disable action.

gdb/testsuite/ChangeLog:

* gdb.ada/sub_bkpt_range.exp: New test scenario.
* gdb.ada/sub_bkpt_range/overload.adb: New test.
* gdb.cp/subbprange.exp: New test scenario.
* gdb.cp/subbprange.cc: New test.

+         "breakpoint info disable 2.8 to 2.6"
  

Comments

Pedro Alves Sept. 27, 2017, 7:03 p.m. UTC | #1
Hi!

(I believe this is a v2.  It's nice to include that
in the email subject, like [PATCH v2]).

On 09/27/2017 03:49 PM, Xavier Roirand wrote:
> Add enable/disable sub breakpoint capability.

I've wanted this feature before!  Thanks for working on this.
We can already end up with disabled locations if a library is unloaded,
and we already try to propagate enabled/disabled status of locations
across breakpoint re-sets.  We were just missing exposing this
to users.

I'm not sure I like the inventing new terminology for this, though,
when we already call these "breakpoint location" throughout.
I.e., why not:

 [RFA] Allow enabling/disabling breakpoint locations

and then talk in terms of multiple locations throughout?

This isn't just a nit for the subject, because you're adding
references to the term to the tests at least.  I can't tell
whether you'd be adding them to the manual too, since it seems
like the manual hunk included in the patch is corrupt.  It's
mentioned in the manual's ChangeLog, though.

Some overall nits & comments:

 - Missing double-space after period in several comments throughout
   the patch.

 - Space-before ( missing in some cases throughout.

 - Tests don't follow GNU coding standards; but they should.

 - "written by" comments in the testcases are stale; in any case, we
   don't add "contributed by" comments to the sources anymore.

 - Missing copyright header in testcases.

 - Several missing full-stop periods in comments in the tests.

 - Are the Ada and C++ tests basically the same?  I wonder
   about maybe sharing most of the code both?  Maybe move
   the actual testing to a procedure called twice, once
   for each language.

> +
> +/* Call FUNC with data parameter on each of the breakpoints present in

s/FUNC/FUNCTION/  

Also there's no data parameter.  Maybe you had one before
function_view-fication.  Just remove that remark.


> +   range defined by BREAKPOINT_NUMBER_START => BREAKPOINT_NUMBER_END.
> +   If BREAKPOINT_NUMBER_START == BREAKPOINT_NUMBER_END. then the
> +   range is just a single breakpoint number.  */
> +
> +static void
> +map_breakpoint_number (int breakpoint_number_start,

I don't get the logic for the function name:

 map_breakpoint_number

Particularly since we have "map_breakpoint_numbers" (plural)
and also because this function works with a number range.

How about "map_breakpoint_number_range" instead?


> +                       int breakpoint_number_end,
> +                       gdb::function_view<void (breakpoint *)> function)
> +{
> +  int num;
> +  struct breakpoint *b, *tmp;

Please move these to the scope that requires them.

> +
> +  if (breakpoint_number_start == 0)
> +    {
> +      warning (_("bad breakpoint number at or near '%d'"),
> +               breakpoint_number_start);
> +    }
> +  else
> +    {
> +      int i;
> +
> +      for (i = breakpoint_number_start; i <= breakpoint_number_end; i++)

Similarly:

     for (int i = breakpoint_number_start

> +        {
> +          bool match = false;
> +
> +          ALL_BREAKPOINTS_SAFE (b, tmp)
> +            if (b->number == i)
> +          {
> +            match = true;
> +            function (b);
> +            break;
> +          }
> +          if (!match)
> +        printf_unfiltered (_("No breakpoint number %d.\n"), i);
> +        }
> +    }
> +}
> +
>  /* Call FUNCTION on each of the breakpoints
>     whose numbers are given in ARGS.  */

> +/* Return the breakpoint structure corresponding to the
> +   BP_NUM and LOC_NUM values.  */

The breakpoint _location_ structure.  The breakpoint structure
would be struct breakpoint.

> +
>  static struct bp_location *
> -find_location_by_number (const char *number)
> +find_location_by_number (int bp_num, int loc_num)
>  {
> -  const char *p1;
> -  int bp_num;
> -  int loc_num;
>    struct breakpoint *b;
>    struct bp_location *loc;
> 
> -  p1 = number;
> -  bp_num = get_number_trailer (&p1, '.');
> -  if (bp_num == 0 || p1[0] != '.')
> -    error (_("Bad breakpoint number '%s'"), number);
> -
>    ALL_BREAKPOINTS (b)
>      if (b->number == bp_num)
>        {
> @@ -14379,25 +14406,152 @@ find_location_by_number (const char *number)
>        }
> 
>    if (!b || b->number != bp_num)
> -    error (_("Bad breakpoint number '%s'"), number);
> +    error (_("Bad breakpoint number '%d'"), bp_num);
> 
> -  /* Skip the dot.  */
> -  ++p1;
> -  const char *save = p1;
> -  loc_num = get_number (&p1);
>    if (loc_num == 0)
> -    error (_("Bad breakpoint location number '%s'"), number);
> +    error (_("Bad breakpoint location number '%d'"), loc_num);
> 
>    --loc_num;
>    loc = b->loc;
>    for (;loc_num && loc; --loc_num, loc = loc->next)
>      ;
>    if (!loc)
> -    error (_("Bad breakpoint location number '%s'"), save);
> +    error (_("Bad breakpoint location number '%d'"), loc_num);
> 
>    return loc;
>  }
> 
> +/* Extract the breakpoint range defined by ARG and return the start of
> +   the breakpoint range defined by BP_NUM_START and BP_LOC_START and end
> +   of the breakpoint range defined by BP_NUM_END and BP_LOC_END.
> +   The range may be any of the following form:
> +
> +   x     where x is breakpoint number.
> +   x-y   where x and y are breakpoint numbers range.
> +   x.y   where x is breakpoint number and z a location number.
> +   x.y-z where x is breakpoint number and y and z a location number
> +         range.  */

Missing description of the return.

> +
> +static int
> +extract_bp_number_and_location (std::string arg, int *bp_num_start,
> +                                int *bp_num_end, int *bp_loc_start,
> +                                int *bp_loc_end)
> +{

The function does unnecessary string copying, starting with
passing ARG by value instead of const reference.

> +  char *tmp_str;
> +  std::string num = std::string (arg);

Copying a std::string results in a heap allocation and a
deep string copy.  There's need for this copy.  The body 
of the function has other similar issues (unnecessary xstrdup'ping),
but I can't go over all of them right now.  Please review the
function and try to eliminate all string copying.
Related, the function uses make_cleanup.  That's strange in
a function that is using std::string.  Please eliminate that.

As for the function's prototype, it may result in clearer
code to pass use a

 struct bp_range { int start; int end; };
or

 std::pair<int> 

for the ranges, like:

extract_bp_number_and_location (const std::string &arg,
                                std::pair<int, int> *bp_num_range,
                                std::pair<int, int> *bp_loc_range)


Also please add spaces around '+', and use /**/ instead of //.

> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 9905ff6..7699adb 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -3896,13 +3896,20 @@ Num     Type           Disp Enb  Address    What
> 
>  Each location can be individually enabled or disabled by passing
>  @var{breakpoint-number}.@var{location-number} as argument to the
> -@code{enable} and @code{disable} commands.  Note that you cannot
> -delete the individual locations from the list, you can only delete the
> -entire list of locations that belong to their parent breakpoint (with
> -the @kbd{delete @var{num}} command, where @var{num} is the number of
> -the parent breakpoint, 1 in the above example).  Disabling or enabling
> -the parent breakpoint (@pxref{Disabling}) affects all of the locations
> -that belong to that breakpoint.
> ++@code{enable} and @code{disable} commands.  It's also possible to
> ++@code{enable} and @code{disable} range of @var{location-number}
> ++breakpoints using a @var{breakpoint-number} and two
> @var{location-number},
> ++in increasing order, separated by a hyphen, like
> ++‘@var{breakpoint-number}.5-7’.
> ++In this case, when a @var{location-number} range is given to this
> ++command, all breakpoints belonging to this @var{breakpoint-number}
> ++and inside that range are operated on.
> ++Note that you cannot delete the individual locations from the list,
> +you can only delete the entire list of locations that belong to their
> +parent breakpoint (with the @kbd{delete @var{num}} command, where
> +@var{num} is the number of the parent breakpoint, 1 in the above example).
> +Disabling or enabling the parent breakpoint (@pxref{Disabling}) affects
> +all of the locations that belong to that breakpoint.


This looks like a diff of a diff?

This should come with a NEWS entry too.

> +if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] !=
> ""} {
> +    return -1
> +}
> +
> +# Returns a buffer corresponding to what gdb reply when

"replies"

> +# asking for 'info breakpoint'. The parameters are all the
> +# existing breakpoints enabled/disable value: 'n' or 'y'.
> +

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 8585f5e..e08de85 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -104,9 +104,17 @@  static void disable_command (char *, int);

  static void enable_command (char *, int);

+static void map_breakpoint_number (int breakpoint_number_start,
+                                   int breakpoint_number_end,
+                                   gdb::function_view<void (breakpoint 
*)>);
+
  static void map_breakpoint_numbers (const char *,
  				    gdb::function_view<void (breakpoint *)>);

+static void enable_disable_command (char *args, int from_tty, bool enable);
+
+static void enable_disable_command (char *args, int from_tty, bool enable);
+
  static void ignore_command (char *, int);

  static int breakpoint_re_set_one (void *);
@@ -14317,13 +14325,52 @@  ignore_command (char *args, int from_tty)
    if (from_tty)
      printf_filtered ("\n");
  }
-
+
+/* Call FUNC with data parameter on each of the breakpoints present in
+   range defined by BREAKPOINT_NUMBER_START => BREAKPOINT_NUMBER_END.
+   If BREAKPOINT_NUMBER_START == BREAKPOINT_NUMBER_END. then the
+   range is just a single breakpoint number.  */
+
+static void
+map_breakpoint_number (int breakpoint_number_start,
+                       int breakpoint_number_end,
+                       gdb::function_view<void (breakpoint *)> function)
+{
+  int num;
+  struct breakpoint *b, *tmp;
+
+  if (breakpoint_number_start == 0)
+    {
+      warning (_("bad breakpoint number at or near '%d'"),
+               breakpoint_number_start);
+    }
+  else
+    {
+      int i;
+
+      for (i = breakpoint_number_start; i <= breakpoint_number_end; i++)
+        {
+          bool match = false;
+
+          ALL_BREAKPOINTS_SAFE (b, tmp)
+    	    if (b->number == i)
+	      {
+	        match = true;
+	        function (b);
+	        break;
+	      }
+          if (!match)
+	    printf_unfiltered (_("No breakpoint number %d.\n"), i);
+        }
+    }
+}
+
  /* Call FUNCTION on each of the breakpoints
     whose numbers are given in ARGS.  */

  static void
  map_breakpoint_numbers (const char *args,
-			gdb::function_view<void (breakpoint *)> function)
+                        gdb::function_view<void (breakpoint *)> function)
  {
    int num;
    struct breakpoint *b, *tmp;
@@ -14339,39 +14386,19 @@  map_breakpoint_numbers (const char *args,
        bool match = false;

        num = parser.get_number ();
-      if (num == 0)
-	{
-	  warning (_("bad breakpoint number at or near '%s'"), p);
-	}
-      else
-	{
-	  ALL_BREAKPOINTS_SAFE (b, tmp)
-	    if (b->number == num)
-	      {
-		match = true;
-		function (b);
-		break;
-	      }
-	  if (!match)
-	    printf_unfiltered (_("No breakpoint number %d.\n"), num);
-	}
+      map_breakpoint_number (num, num, function);
      }
  }

+/* Return the breakpoint structure corresponding to the
+   BP_NUM and LOC_NUM values.  */
+
  static struct bp_location *
-find_location_by_number (const char *number)
+find_location_by_number (int bp_num, int loc_num)
  {
-  const char *p1;
-  int bp_num;
-  int loc_num;
    struct breakpoint *b;
    struct bp_location *loc;

-  p1 = number;
-  bp_num = get_number_trailer (&p1, '.');
-  if (bp_num == 0 || p1[0] != '.')
-    error (_("Bad breakpoint number '%s'"), number);
-
    ALL_BREAKPOINTS (b)
      if (b->number == bp_num)
        {
@@ -14379,25 +14406,152 @@  find_location_by_number (const char *number)
        }

    if (!b || b->number != bp_num)
-    error (_("Bad breakpoint number '%s'"), number);
+    error (_("Bad breakpoint number '%d'"), bp_num);

-  /* Skip the dot.  */
-  ++p1;
-  const char *save = p1;
-  loc_num = get_number (&p1);
    if (loc_num == 0)
-    error (_("Bad breakpoint location number '%s'"), number);
+    error (_("Bad breakpoint location number '%d'"), loc_num);

    --loc_num;
    loc = b->loc;
    for (;loc_num && loc; --loc_num, loc = loc->next)
      ;
    if (!loc)
-    error (_("Bad breakpoint location number '%s'"), save);
+    error (_("Bad breakpoint location number '%d'"), loc_num);

    return loc;
  }

+/* Extract the breakpoint range defined by ARG and return the start of
+   the breakpoint range defined by BP_NUM_START and BP_LOC_START and end
+   of the breakpoint range defined by BP_NUM_END and BP_LOC_END.
+   The range may be any of the following form:
+
+   x     where x is breakpoint number.
+   x-y   where x and y are breakpoint numbers range.
+   x.y   where x is breakpoint number and z a location number.
+   x.y-z where x is breakpoint number and y and z a location number
+         range.  */
+
+static int
+extract_bp_number_and_location (std::string arg, int *bp_num_start,
+                                int *bp_num_end, int *bp_loc_start,
+                                int *bp_loc_end)
+{
+  char *tmp_str;
+  std::string num = std::string (arg);
+
+  std::size_t dot = num.find (".");
+
+  if (dot != std::string::npos)
+    {
+      /* Handle x.y and x.y-z cases.  */
+      std::size_t dash;
+      std::string bp_loc;
+
+      if (num.length () == dot+1 || dot == 0)
+        return 1;
+
+      dash = num.find ("-", dot+1);
+
+      bp_loc = num.substr (dot + 1);
+      tmp_str = xstrdup (num.substr (0,dot).c_str ());
+      if (tmp_str != NULL)
+        make_cleanup (xfree, tmp_str);
+
+      *bp_num_start = get_number (&tmp_str);
+      *bp_num_end = *bp_num_start;
+
+      if (dash != std::string::npos)
+        {
+          // bp_loc is range (x-z)
+          if (num.length () == dash+1)
+            return 1;
+          dash = bp_loc.find ("-");
+          tmp_str = xstrdup (bp_loc.substr (0,dash).c_str ());
+          if (tmp_str != NULL)
+            make_cleanup (xfree, tmp_str);
+          *bp_loc_start = get_number (&tmp_str);
+          tmp_str = xstrdup (bp_loc.substr (dash+1).c_str ());
+          if (tmp_str != NULL)
+            make_cleanup (xfree, tmp_str);
+          *bp_loc_end = get_number (&tmp_str);
+        }
+      else
+        {
+          // bp_loc is single value
+        tmp_str = xstrdup (bp_loc.c_str ());
+        if (tmp_str != NULL)
+          make_cleanup (xfree, tmp_str);
+        *bp_loc_end = get_number (&tmp_str);
+        *bp_loc_start = *bp_loc_end;
+        }
+    }
+  else
+  {
+      /* Handle x and x-y cases.  */
+      std::size_t dash;
+
+      dash = num.find ("-");
+      *bp_loc_start = 0;
+      *bp_loc_end = 0;
+      if (dash != std::string::npos)
+        {
+          if (num.length () == dash+1 || dash == 0)
+            return 1;
+
+          tmp_str = xstrdup (num.substr (0,dash).c_str ());
+          if (tmp_str != NULL)
+            make_cleanup (xfree, tmp_str);
+          *bp_num_start = get_number (&tmp_str);
+          tmp_str = xstrdup (num.substr (dash+1).c_str ());
+          if (tmp_str != NULL)
+            make_cleanup (xfree, tmp_str);
+          *bp_num_end = get_number (&tmp_str);
+        }
+      else
+        {
+          tmp_str = xstrdup (num.c_str ());
+          if (tmp_str != NULL)
+            make_cleanup (xfree, tmp_str);
+          *bp_num_start = get_number (&tmp_str);
+          *bp_num_end = *bp_num_start;
+        }
+    }
+  return 0;
+}
+
+/* Enable or disable a breakpoint using BP_NUMB, LOC_NUM and enable.  */
+
+static void
+enable_disable_bp_num_loc (int bp_num, int loc_num, bool enable)
+{
+  struct bp_location *loc = find_location_by_number (bp_num, loc_num);
+  if (loc != NULL)
+    {
+      if (loc->enabled != enable)
+        {
+          loc->enabled = enable;
+          mark_breakpoint_location_modified (loc);
+        }
+      if (target_supports_enable_disable_tracepoint ()
+          && current_trace_status ()->running && loc->owner
+          && is_tracepoint (loc->owner))
+        target_disable_tracepoint (loc);
+    }
+  update_global_location_list (UGLL_DONT_INSERT);
+}
+
+/* Enable or disable a sub breakpoint range. It uses BP_NUM, and
+   BP_LOC_START for the start of the range, BP_LOC_END for the end of
+   the range and enable.  */
+
+static void
+enable_disable_sub_breakpoint_range (int bp_num, int bp_loc_start,
+				     int bp_loc_end, bool enable)
+{
+  for (int i = bp_loc_start; i <= bp_loc_end; i++)
+    enable_disable_bp_num_loc (bp_num, i, enable);
+}

  /* Set ignore-count of breakpoint number BPTNUM to COUNT.
     If from_tty is nonzero, it prints a message to that effect,
@@ -14431,8 +14585,12 @@  disable_breakpoint (struct breakpoint *bpt)
    observer_notify_breakpoint_modified (bpt);
  }

+/* Enable or disable breakpoint defined in ARGS. Breakpoint may be
+   any of the form defined in extract_bp_number_and_location.
+   ENABLE enable or disable breakpoint.  */
+
  static void
-disable_command (char *args, int from_tty)
+enable_disable_command (char *args, int from_tty, bool enable)
  {
    if (args == 0)
      {
@@ -14440,43 +14598,62 @@  disable_command (char *args, int from_tty)

        ALL_BREAKPOINTS (bpt)
  	if (user_breakpoint_p (bpt))
-	  disable_breakpoint (bpt);
+          {
+            if (enable)
+              enable_breakpoint (bpt);
+            else
+              disable_breakpoint (bpt);
+          }
      }
    else
      {
        std::string num = extract_arg (&args);

        while (!num.empty ())
-	{
-	  if (num.find ('.') != std::string::npos)
-	    {
-	      struct bp_location *loc = find_location_by_number (num.c_str ());
-
-	      if (loc)
-		{
-		  if (loc->enabled)
-		    {
-		      loc->enabled = 0;
-		      mark_breakpoint_location_modified (loc);
-		    }
-		  if (target_supports_enable_disable_tracepoint ()
-		      && current_trace_status ()->running && loc->owner
-		      && is_tracepoint (loc->owner))
-		    target_disable_tracepoint (loc);
-		}
-	      update_global_location_list (UGLL_DONT_INSERT);
-	    }
-	  else
-	    map_breakpoint_numbers
-	      (num.c_str (), [&] (breakpoint *b)
-	       {
-		 iterate_over_related_breakpoints (b, disable_breakpoint);
-	       });
-	  num = extract_arg (&args);
-	}
+        {
+          int bp_num_start;
+          int bp_num_end;
+          int bp_loc_start;
+          int bp_loc_end;
+
+          int err_ret
+            = extract_bp_number_and_location (num.c_str(),
+                                              &bp_num_start, &bp_num_end,
+                                              &bp_loc_start, &bp_loc_end);
+          if (err_ret)
+            error (_("bad breakpoint number: '%s'"), num.c_str ());
+          else
+            {
+              if (bp_loc_start == bp_loc_end && bp_loc_start == 0)
+                {
+                  /* Handle breakpoint with format x or x-z only.  */
+                  map_breakpoint_number (bp_num_start, bp_num_end,
+                                         enable ? enable_breakpoint :
+                                           disable_breakpoint);
+                }
+              else
+                {
+                  /* Handle breakpoint with format is x.y or x.y-z */
+                  enable_disable_sub_breakpoint_range
+                    (bp_num_start, bp_loc_start, bp_loc_end, enable);
+                }
+            }
+          num = extract_arg (&args);
+        }
      }
  }

+/* The disable command disables the specified breakpoints (or all defined
+   breakpoints) so they once stop be effective in stopping
+   the inferior. ARGS may be any of the form defined in
+   extract_bp_number_and_location. */
+
+static void
+disable_command (char *args, int from_tty)
+{
+  enable_disable_command (args, from_tty, false);
+}
+
  static void
  enable_breakpoint_disp (struct breakpoint *bpt, enum bpdisp disposition,
  			int count)
@@ -14549,52 +14726,13 @@  enable_breakpoint (struct breakpoint *bpt)

  /* The enable command enables the specified breakpoints (or all defined
     breakpoints) so they once again become (or continue to be) effective
-   in stopping the inferior.  */
+   in stopping the inferior. ARGS may be any of the form defined in
+   extract_bp_number_and_location. */

  static void
  enable_command (char *args, int from_tty)
  {
-  if (args == 0)
-    {
-      struct breakpoint *bpt;
-
-      ALL_BREAKPOINTS (bpt)
-	if (user_breakpoint_p (bpt))
-	  enable_breakpoint (bpt);
-    }
-  else
-    {
-      std::string num = extract_arg (&args);
-
-      while (!num.empty ())
-	{
-	  if (num.find ('.') != std::string::npos)
-	    {
-	      struct bp_location *loc = find_location_by_number (num.c_str ());
-
-	      if (loc)
-		{
-		  if (!loc->enabled)
-		    {
-		      loc->enabled = 1;
-		      mark_breakpoint_location_modified (loc);
-		    }
-		  if (target_supports_enable_disable_tracepoint ()
-		      && current_trace_status ()->running && loc->owner
-		      && is_tracepoint (loc->owner))
-		    target_enable_tracepoint (loc);
-		}
-	      update_global_location_list (UGLL_MAY_INSERT);
-	    }
-	  else
-	    map_breakpoint_numbers
-	      (num.c_str (), [&] (breakpoint *b)
-	       {
-		 iterate_over_related_breakpoints (b, enable_breakpoint);
-	       });
-	  num = extract_arg (&args);
-	}
-    }
+  enable_disable_command (args, from_tty, true);
  }

  static void
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9905ff6..7699adb 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3896,13 +3896,20 @@  Num     Type           Disp Enb  Address    What

  Each location can be individually enabled or disabled by passing
  @var{breakpoint-number}.@var{location-number} as argument to the
-@code{enable} and @code{disable} commands.  Note that you cannot
-delete the individual locations from the list, you can only delete the
-entire list of locations that belong to their parent breakpoint (with
-the @kbd{delete @var{num}} command, where @var{num} is the number of
-the parent breakpoint, 1 in the above example).  Disabling or enabling
-the parent breakpoint (@pxref{Disabling}) affects all of the locations
-that belong to that breakpoint.
++@code{enable} and @code{disable} commands.  It's also possible to
++@code{enable} and @code{disable} range of @var{location-number}
++breakpoints using a @var{breakpoint-number} and two @var{location-number},
++in increasing order, separated by a hyphen, like
++‘@var{breakpoint-number}.5-7’.
++In this case, when a @var{location-number} range is given to this
++command, all breakpoints belonging to this @var{breakpoint-number}
++and inside that range are operated on.
++Note that you cannot delete the individual locations from the list,
+you can only delete the entire list of locations that belong to their
+parent breakpoint (with the @kbd{delete @var{num}} command, where
+@var{num} is the number of the parent breakpoint, 1 in the above example).
+Disabling or enabling the parent breakpoint (@pxref{Disabling}) affects
+all of the locations that belong to that breakpoint.

  @cindex pending breakpoints
  It's quite common to have a breakpoint inside a shared library.
diff --git a/gdb/testsuite/gdb.ada/sub_bkpt_range.exp 
b/gdb/testsuite/gdb.ada/sub_bkpt_range.exp
new file mode 100644
index 0000000..9f0ebe9
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/sub_bkpt_range.exp
@@ -0,0 +1,148 @@ 
+# Copyright 2017 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/>.
+
+load_lib "ada.exp"
+
+if { [skip_ada_tests] } { return -1 }
+
+standard_ada_testfile overload
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} {
+    return -1
+}
+
+# Returns a buffer corresponding to what gdb reply when
+# asking for 'info breakpoint'. The parameters are all the
+# existing breakpoints enabled/disable value: 'n' or 'y'.
+
+proc set_info_breakpoint_reply {b1 b2 b21 b22 b23 b24} {
+
+    set buf "Num     Type\[ \]+Disp Enb Address\[ \]+What.*
+1\[\t \]+breakpoint     keep $b1.* in overload at .*
+\[\t \]+breakpoint already hit 1 time.*
+2\[\t \]+breakpoint\[\t \]+keep $b2\[\t \]+<MULTIPLE>.*
+2.1\[\t \]+$b21.*
+2.2\[\t \]+$b22.*
+2.3\[\t \]+$b23.*
+2.4\[\t \]+$b24.*"
+
+    return $buf
+}
+
+clean_restart ${testfile}
+
+set bp_location [gdb_get_line_number "STOP" ${testdir}/overload.adb]
+runto "overload.adb:$bp_location"
+
+gdb_test "break Raise_To_Power" \
+    "Breakpoint \[0-9\]+ at $hex: Raise_To_Power. .4 locations." \
+    "set breakpoint at Raise_To_Power"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info"
+
+# Check that we can disable a breakpoint
+gdb_test_no_output "disable 1"
+
+gdb_test "info break" [set_info_breakpoint_reply n y y y y y] \
+         "breakpoint info disable bkpt 1"
+
+# Check that we can enable a breakpoint
+gdb_test_no_output "enable 1"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 1"
+
+# Check that we can disable a breakpoint with sub-breakpoints
+gdb_test_no_output "disable 2"
+
+gdb_test "info break" [set_info_breakpoint_reply y n y y y y] \
+         "breakpoint info disable bkpt 2"
+
+# Check that we can enable a breakpoint with sub-breakpoints
+gdb_test_no_output "enable 2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 2"
+
+# Check that we can disable a sub-breakpoint
+gdb_test_no_output "disable 2.2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable bkpt 2.2"
+
+# Check that we can enable a sub-breakpoint
+gdb_test_no_output "enable 2.2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 2.2"
+
+# Check that we can disable a sub-breakpoint range
+gdb_test_no_output "disable 2.2-3"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n n y] \
+         "breakpoint info disable bkpt 2.2 to 2.3"
+
+# Check that we can enable a sub-breakpoint range
+gdb_test_no_output "enable 2.2-3"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enaable bkpt 2.2 to 2.3"
+
+# Check that we can disable a sub-breakpoint range reduced
+# to a single location.
+gdb_test_no_output "disable 2.2-2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.2 to 2.2"
+
+# Check that we can disable a sub-breakpoint range with max >
+# existing sub breakpoint location.
+gdb_test "disable 2.3-5" "Bad breakpoint location number '$decimal'" \
+         "disable sub-breakpoint range with max > existing"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n n n] \
+         "breakpoint info disable 2.3 to 2.5"
+
+# Check that we can enable a sub-breakpoint range with max >
+# existing sub breakpoint location.
+gdb_test "enable 2.3-5" "Bad breakpoint location number '$decimal'" \
+         "enable sub-breakpoint range with max > existing"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info enable 2.3 to 2.5"
+
+# Check that disabling an reverse sub-breakpoint range does
+# not work
+gdb_test_no_output "disable 2.3-2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.3 to 2.3"
+
+# Check that disabling an unvalid sub-breakpoint range does
+# not cause unexpected behavior.
+gdb_test "disable 2.6-7" "Bad breakpoint location number '$decimal'" \
+         "disable an unvalid sub-breakpoint range"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.6 to 2.7"
+
+# Check that disabling an invalid sub-breakpoint range does not
+# cause trouble.
+gdb_test_no_output "disable 2.8-6"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.8 to 2.6"
+
diff --git a/gdb/testsuite/gdb.ada/sub_bkpt_range/overload.adb 
b/gdb/testsuite/gdb.ada/sub_bkpt_range/overload.adb
new file mode 100644
index 0000000..da81446
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/sub_bkpt_range/overload.adb
@@ -0,0 +1,47 @@ 
+--  Copyright 2017 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/>.
+
+procedure Overload is
+
+   MyInt : Integer;
+   MyFloat : Float;
+
+   function Raise_To_Power(Index : Integer) return Integer is
+   begin
+      return Index * Index;
+   end Raise_To_Power;
+
+   function Raise_To_Power(Value : Float) return Float is
+   begin
+      return Value * Value * Value;
+   end Raise_To_Power;
+
+   procedure Raise_To_Power(Index: in Integer; Result: out Integer) is
+   begin
+      Result := Index * Index * Index;
+   end Raise_To_Power;
+
+   procedure Raise_To_Power(Value: in Float; Result: out Float) is
+   begin
+      Result := Value * Value;
+   end Raise_To_Power;
+
+begin
+   MyInt := Raise_To_Power(4);     -- uses Integer function STOP
+   Raise_To_Power(2, MyInt);       -- uses Integer procedure
+
+   MyFloat := Raise_To_Power(5.1);   -- uses Float function
+   Raise_To_Power(3.11, MyFloat);    -- uses Float procedure
+end Overload;
diff --git a/gdb/testsuite/gdb.cp/subbprange.cc 
b/gdb/testsuite/gdb.cp/subbprange.cc
new file mode 100644
index 0000000..e0bf547
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/subbprange.cc
@@ -0,0 +1,30 @@ 
+#include <stddef.h>
+
+class foo {
+public:
+  static int overload (void);
+  static int overload (char);
+  static int overload (int);
+  static int overload (double);
+};
+
+void marker1() {}
+
+int main ()
+{
+    foo::overload();
+    foo::overload(111);
+    foo::overload('h');
+    foo::overload(3.14);
+
+    marker1(); // marker1-returns-here
+
+    return 0;
+}
+
+/* Some functions to test overloading by varying one argument type. */
+
+int foo::overload (void)                {  return 1; }
+int foo::overload (char arg)            { arg = 0; return 2;}
+int foo::overload (int arg)             { arg = 0; return 3;}
+int foo::overload (double arg)          { arg = 0; return 4;}
diff --git a/gdb/testsuite/gdb.cp/subbprange.exp 
b/gdb/testsuite/gdb.cp/subbprange.exp
new file mode 100644
index 0000000..ab7d755
--- /dev/null
+++ b/gdb/testsuite/gdb.cp/subbprange.exp
@@ -0,0 +1,163 @@ 
+# Copyright 2017 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/>.
+
+# written by Elena Zannoni (ezannoni@cygnus.com)
+# Rewritten by Michael Chastain <mec.gnu@mindspring.com>
+
+# This file is part of the gdb testsuite
+
+# Tests for overloaded member functions.
+
+set ws "\[\r\n\t \]+"
+set nl "\[\r\n\]+"
+
+
+if { [skip_cplus_tests] } { continue }
+
+standard_testfile .cc
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug 
c++}]} {
+    return -1
+}
+
+# Set it up at a breakpoint so we can play with the variable values.
+
+if ![runto 'marker1'] then {
+    perror "couldn't run to marker1"
+    continue
+}
+
+# Prevent symbol on address 0x0 being printed.
+gdb_test_no_output "set print symbol off"
+
+gdb_test "up" ".*main.*" "up from marker1"
+
+# Returns a buffer corresponding to what gdb reply when
+# asking for 'info breakpoint'. The parameters are all the
+# existing breakpoints enabled/disable value: 'n' or 'y'.
+
+proc set_info_breakpoint_reply {b1 b2 b21 b22 b23 b24} {
+
+    set buf "Num     Type\[ \]+Disp Enb Address\[ \]+What.*
+1\[\t \]+breakpoint     keep $b1.* in marker1\\(\\) at .*
+\[\t \]+breakpoint already hit 1 time.*
+2\[\t \]+breakpoint\[\t \]+keep $b2\[\t \]+<MULTIPLE>.*
+2.1\[\t \]+$b21.*
+2.2\[\t \]+$b22.*
+2.3\[\t \]+$b23.*
+2.4\[\t \]+$b24.*"
+
+    return $buf
+}
+
+gdb_test "break foo::overload" \
+    "Breakpoint \[0-9\]+ at $hex: foo::overload. .4 locations." \
+    "set breakpoint at overload"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info"
+
+# Check that we can disable a breakpoint
+gdb_test_no_output "disable 1"
+
+gdb_test "info break" [set_info_breakpoint_reply n y y y y y] \
+         "breakpoint info disable bkpt 1"
+
+# Check that we can enable a breakpoint
+gdb_test_no_output "enable 1"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 1"
+
+# Check that we can disable a breakpoint with sub-breakpoints
+gdb_test_no_output "disable 2"
+
+gdb_test "info break" [set_info_breakpoint_reply y n y y y y] \
+         "breakpoint info disable bkpt 2"
+
+# Check that we can enable a breakpoint with sub-breakpoints
+gdb_test_no_output "enable 2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 2"
+
+# Check that we can disable a sub-breakpoint
+gdb_test_no_output "disable 2.2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable bkpt 2.2"
+
+# Check that we can enable a sub-breakpoint
+gdb_test_no_output "enable 2.2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enable bkpt 2.2"
+
+# Check that we can disable a sub-breakpoint range
+gdb_test_no_output "disable 2.2-3"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n n y] \
+         "breakpoint info disable bkpt 2.2 to 2.3"
+
+# Check that we can enable a sub-breakpoint range
+gdb_test_no_output "enable 2.2-3"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y y y y] \
+         "breakpoint info enaable bkpt 2.2 to 2.3"
+
+# Check that we can disable a sub-breakpoint range reduced
+# to a single location.
+gdb_test_no_output "disable 2.2-2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.2 to 2.2"
+
+# Check that we can disable a sub-breakpoint range with max >
+# existing sub breakpoint location.
+gdb_test "disable 2.3-5" "Bad breakpoint location number '$decimal'" \
+         "disable sub-breakpoint range with max > existing"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n n n] \
+         "breakpoint info disable 2.3 to 2.5"
+
+# Check that we can enable a sub-breakpoint range with max >
+# existing sub breakpoint location.
+gdb_test "enable 2.3-5" "Bad breakpoint location number '$decimal'" \
+         "enable sub-breakpoint range with max > existing"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info enable 2.3 to 2.5"
+
+# Check that disabling an reverse sub-breakpoint range does
+# not work
+gdb_test_no_output "disable 2.3-2"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.3 to 2.3"
+
+# Check that disabling an unvalid sub-breakpoint range does
+# not cause unexpected behavior.
+gdb_test "disable 2.6-7" "Bad breakpoint location number '$decimal'" \
+         "disable an unvalid sub-breakpoint range"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \
+         "breakpoint info disable 2.6 to 2.7"
+
+# Check that disabling an invalid sub-breakpoint range does not
+# cause trouble.
+gdb_test_no_output "disable 2.8-6"
+
+gdb_test "info break" [set_info_breakpoint_reply y y y n y y] \