Allow enabling/disabling breakpoint location ranges
authorXavier Roirand <roirand@adacore.com>
Tue, 7 Nov 2017 11:00:31 +0000 (11:00 +0000)
committerPedro Alves <palves@redhat.com>
Tue, 7 Nov 2017 11:00:31 +0000 (11:00 +0000)
When a breakpoint has multiple locations, like e.g.:

 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>()...
 [....]
 1.5                  y    0x080487fa in void foo<long>()...

it's possible to enable/disable the individual locations using the
'<breakpoint_number>.<location_number>' syntax, like e.g.:

 (gdb) disable 1.2 1.3 1.4 1.5

That's inconvenient when you have a long list of locations to disable,
however.

This patch adds shorthand for the above, by making it possible to
specify a range of locations with the following syntax (similar to
thread id ranges):

 <breakpoint_number>.<first_location_number>-<last_location_number>

For example, the command above can now be simplified to:

 (gdb) disable 1.2-5

gdb/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
    Pedro Alves  <palves@redhat.com>

* breakpoint.c (map_breakpoint_number_range): New, factored out
from ...
(map_breakpoint_numbers): ... here.
(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)
(enable_disable_breakpoint_location_range)
(enable_disable_command): New functions, factored out ...
(enable_command, disable_command): ... these functions, and
adjusted to support ranges.
* NEWS: Document enable/disable breakpoint location range feature.

gdb/doc/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
    Pedro Alves  <palves@redhat.com>

* gdb.texinfo (Set Breaks): Document support for breakpoint
location ranges in the enable/disable commands.

gdb/testsuite/ChangeLog:
2017-11-07  Xavier Roirand  <roirand@adacore.com>
    Pedro Alves  <palves@redhat.com>

* gdb.base/ena-dis-br.exp: Add reference to
gdb.cp/ena-dis-br-range.exp.
* gdb.cp/ena-dis-br-range.exp: New file.
* gdb.cp/ena-dis-br-range.cc: New file.

gdb/ChangeLog
gdb/NEWS
gdb/breakpoint.c
gdb/doc/ChangeLog
gdb/doc/gdb.texinfo
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/ena-dis-br.exp
gdb/testsuite/gdb.cp/ena-dis-br-range.cc [new file with mode: 0644]
gdb/testsuite/gdb.cp/ena-dis-br-range.exp [new file with mode: 0644]

index 077369cec9a0890b300dd3852015fe3b2ddfe0b5..1770b1f02a8e94ba4fe598fb40bb8b81ca0847bb 100644 (file)
@@ -1,3 +1,19 @@
+2017-11-07  Xavier Roirand  <roirand@adacore.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * breakpoint.c (map_breakpoint_number_range): New, factored out
+       from ...
+       (map_breakpoint_numbers): ... here.
+       (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)
+       (enable_disable_breakpoint_location_range)
+       (enable_disable_command): New functions, factored out ...
+       (enable_command, disable_command): ... these functions, and
+       adjusted to support ranges.
+       * NEWS: Document enable/disable breakpoint location range feature.
+
 2017-11-06  Luis Machado  <luis.machado@linaro.org>
 
        * MAINTAINERS (Write After Approval): Update my e-mail address.
index fbf55917812f571d69fa784712b05ed6501b2b0d..aadb7a3c36f4fd56b93a6228e77a39827a9eabc4 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -75,6 +75,9 @@ QSetWorkingDir
 * The "maintenance selftest" command now takes an optional argument to
   filter the tests to be run.
 
+* The "enable", and "disable" commands now accept a range of
+  breakpoint locations, e.g. "enable 1.3-5".
+
 * New commands
 
 set|show cwd
index adc89505917fc9cef2bad44a59309916db3007ed..e100f431913183d4a8a411619c87799b07341419 100644 (file)
@@ -14131,59 +14131,66 @@ ignore_command (char *args, int from_tty)
     printf_filtered ("\n");
 }
 \f
-/* Call FUNCTION on each of the breakpoints
-   whose numbers are given in ARGS.  */
+
+/* Call FUNCTION on each of the breakpoints with numbers in the range
+   defined by BP_NUM_RANGE (an inclusive range).  */
 
 static void
-map_breakpoint_numbers (const char *args,
-                       gdb::function_view<void (breakpoint *)> function)
+map_breakpoint_number_range (std::pair<int, int> bp_num_range,
+                            gdb::function_view<void (breakpoint *)> function)
 {
-  int num;
-  struct breakpoint *b, *tmp;
-
-  if (args == 0 || *args == '\0')
-    error_no_arg (_("one or more breakpoint numbers"));
-
-  number_or_range_parser parser (args);
-
-  while (!parser.finished ())
+  if (bp_num_range.first == 0)
+    {
+      warning (_("bad breakpoint number at or near '%d'"),
+              bp_num_range.first);
+    }
+  else
     {
-      const char *p = parser.cur_tok ();
-      bool match = false;
+      struct breakpoint *b, *tmp;
 
-      num = parser.get_number ();
-      if (num == 0)
-       {
-         warning (_("bad breakpoint number at or near '%s'"), p);
-       }
-      else
+      for (int i = bp_num_range.first; i <= bp_num_range.second; i++)
        {
+         bool match = false;
+
          ALL_BREAKPOINTS_SAFE (b, tmp)
-           if (b->number == num)
+           if (b->number == i)
              {
                match = true;
                function (b);
                break;
              }
          if (!match)
-           printf_unfiltered (_("No breakpoint number %d.\n"), num);
+           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)
+{
+  if (args == NULL || *args == '\0')
+    error_no_arg (_("one or more breakpoint numbers"));
+
+  number_or_range_parser parser (args);
+
+  while (!parser.finished ())
+    {
+      int num = parser.get_number ();
+      map_breakpoint_number_range (std::make_pair (num, num), function);
+    }
+}
+
+/* Return the breakpoint location 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)
@@ -14192,25 +14199,153 @@ 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);
-    
-  return loc;  
+  int n = 0;
+  for (bp_location *loc = b->loc; loc != NULL; loc = loc->next)
+    if (++n == loc_num)
+      return loc;
+
+  error (_("Bad breakpoint location number '%d'"), loc_num);
 }
 
+/* Extract the breakpoint/location range specified by ARG.  Returns
+   the breakpoint range in BP_NUM_RANGE, and the location range in
+   BP_LOC_RANGE.
+
+   ARG may be in any of the following forms:
+
+   x     where 'x' is a breakpoint number.
+   x-y   where 'x' and 'y' specify a breakpoint numbers range.
+   x.y   where 'x' is a breakpoint number and 'y' a location number.
+   x.y-z where 'x' is a breakpoint number and 'y' and 'z' specify a
+        location number range.
+*/
+
+static bool
+extract_bp_number_and_location (const std::string &arg,
+                               std::pair<int, int> &bp_num_range,
+                               std::pair<int, int> &bp_loc_range)
+{
+  std::string::size_type dot = arg.find ('.');
+
+  if (dot != std::string::npos)
+    {
+      /* Handle 'x.y' and 'x.y-z' cases.  */
+
+      if (arg.length () == dot + 1 || dot == 0)
+       error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
+
+      const char *ptb = arg.c_str ();
+      int bp_num = get_number_trailer (&ptb, '.');
+      if (bp_num == 0)
+       error (_("Bad breakpoint number '%s'"), arg.c_str ());
+
+      bp_num_range.first = bp_num;
+      bp_num_range.second = bp_num;
+
+      const char *bp_loc = &arg[dot + 1];
+      std::string::size_type dash = arg.find ('-', dot + 1);
+      if (dash != std::string::npos)
+       {
+         /* bp_loc is a range (x-z).  */
+         if (arg.length () == dash + 1)
+           error (_("bad breakpoint number at or near: '%s'"), bp_loc);
+
+         const char *ptlf = bp_loc;
+         bp_loc_range.first = get_number_trailer (&ptlf, '-');
+
+         const char *ptls = &arg[dash + 1];
+         bp_loc_range.second = get_number_trailer (&ptls, '\0');
+       }
+      else
+       {
+         /* bp_loc is a single value.  */
+         const char *ptls = bp_loc;
+         bp_loc_range.first = get_number_trailer (&ptls, '\0');
+         if (bp_loc_range.first == 0)
+           {
+             warning (_("bad breakpoint number at or near '%s'"), arg.c_str ());
+             return false;
+           }
+         bp_loc_range.second = bp_loc_range.first;
+       }
+    }
+  else
+    {
+      /* Handle x and x-y cases.  */
+      std::string::size_type dash = arg.find ('-');
+      if (dash != std::string::npos)
+       {
+         if (arg.length () == dash + 1 || dash == 0)
+           error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
+
+         const char *ptf = arg.c_str ();
+         bp_num_range.first = get_number_trailer (&ptf, '-');
+
+         const char *pts = &arg[dash + 1];
+         bp_num_range.second = get_number_trailer (&pts, '\0');
+       }
+      else
+       {
+         const char *ptf = arg.c_str ();
+         bp_num_range.first = get_number (&ptf);
+         if (bp_num_range.first == 0)
+           {
+             warning (_("bad breakpoint number at or near '%s'"), arg.c_str ());
+             return false;
+           }
+         bp_num_range.second = bp_num_range.first;
+       }
+      bp_loc_range.first = 0;
+      bp_loc_range.second = 0;
+    }
+
+  if (bp_num_range.first == 0 || bp_num_range.second == 0)
+    error (_("bad breakpoint number at or near: '%s'"), arg.c_str ());
+
+  return true;
+}
+
+/* Enable or disable a breakpoint location BP_NUM.LOC_NUM.  ENABLE
+   specifies whether to enable or disable.  */
+
+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 range of breakpoint locations.  BP_NUM is the
+   number of the breakpoint, and BP_LOC_RANGE specifies the
+   (inclusive) range of location numbers of that breakpoint to
+   enable/disable.  ENABLE specifies whether to enable or disable the
+   location.  */
+
+static void
+enable_disable_breakpoint_location_range (int bp_num,
+                                         std::pair<int, int> &bp_loc_range,
+                                         bool enable)
+{
+  for (int i = bp_loc_range.first; i <= bp_loc_range.second; 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,
@@ -14244,8 +14379,13 @@ disable_breakpoint (struct breakpoint *bpt)
   observer_notify_breakpoint_modified (bpt);
 }
 
+/* Enable or disable the breakpoint(s) or breakpoint location(s)
+   specified in ARGS.  ARGS may be in any of the formats handled by
+   extract_bp_number_and_location.  ENABLE specifies whether to enable
+   or disable the breakpoints/locations.  */
+
 static void
-disable_command (const char *args, int from_tty)
+enable_disable_command (const char *args, int from_tty, bool enable)
 {
   if (args == 0)
     {
@@ -14253,7 +14393,12 @@ disable_command (const 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
     {
@@ -14261,35 +14406,43 @@ disable_command (const char *args, int from_tty)
 
       while (!num.empty ())
        {
-         if (num.find ('.') != std::string::npos)
-           {
-             struct bp_location *loc = find_location_by_number (num.c_str ());
+         std::pair<int, int> bp_num_range, bp_loc_range;
 
-             if (loc)
+         if (extract_bp_number_and_location (num, bp_num_range, bp_loc_range))
+           {
+             if (bp_loc_range.first == bp_loc_range.second
+                 && bp_loc_range.first == 0)
                {
-                 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);
+                 /* Handle breakpoint ids with formats 'x' or 'x-z'.  */
+                 map_breakpoint_number_range (bp_num_range,
+                                              enable
+                                              ? enable_breakpoint
+                                              : disable_breakpoint);
+               }
+             else
+               {
+                 /* Handle breakpoint ids with formats 'x.y' or
+                    'x.y-z'.  */
+                 enable_disable_breakpoint_location_range
+                   (bp_num_range.first, bp_loc_range, enable);
                }
-             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);
        }
     }
 }
 
+/* The disable command disables the specified breakpoints/locations
+   (or all defined breakpoints) so they're no longer effective in
+   stopping the inferior.  ARGS may be in any of the forms defined in
+   extract_bp_number_and_location.  */
+
+static void
+disable_command (const 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)
@@ -14360,54 +14513,15 @@ enable_breakpoint (struct breakpoint *bpt)
   enable_breakpoint_disp (bpt, bpt->disposition, 0);
 }
 
-/* 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.  */
+/* The enable command enables the specified breakpoints/locations (or
+   all defined breakpoints) so they once again become (or continue to
+   be) effective in stopping the inferior.  ARGS may be in any of the
+   forms defined in extract_bp_number_and_location.  */
 
 static void
 enable_command (const 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
index e3ea4ec614e997baac565d5f30ace804f4597ac8..98eab725f75ba9474c7dc9103145b51ebc1ae050 100644 (file)
@@ -1,3 +1,9 @@
+2017-11-07  Xavier Roirand  <roirand@adacore.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdb.texinfo (Set Breaks): Document support for breakpoint
+       location ranges in the enable/disable commands.
+
 2017-10-04  Sergio Durigan Junior  <sergiodj@redhat.com>
 
        * gdb.texinfo (Starting your Program) <The working directory.>:
index bfeb7a9a35d1351195961274b775344ffdbb49e7..29d47892fc7aa28feb4c862f897cdbccc92d5eff 100644 (file)
@@ -3927,15 +3927,17 @@ Num     Type           Disp Enb  Address    What
 1.2                         y    0x080486ca in void foo<double>() at t.cc:8
 @end smallexample
 
-Each location can be individually enabled or disabled by passing
+You cannot delete the individual locations from a breakpoint.  However,
+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} a range of @var{location-number}
+locations using a @var{breakpoint-number} and two @var{location-number}s,
+in increasing order, separated by a hyphen, like
+@kbd{@var{breakpoint-number}.@var{location-number1}-@var{location-number2}},
+in which case @value{GDBN} acts on all the locations in the range (inclusive).
+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.
index 371e98b4ec14189504623c1b285017471b6e08cf..6468e52bec38e2c9c5e9967945df3580afcbd331 100644 (file)
@@ -1,3 +1,11 @@
+2017-11-07  Xavier Roirand  <roirand@adacore.com>
+           Pedro Alves  <palves@redhat.com>
+
+       * gdb.base/ena-dis-br.exp: Add reference to
+       gdb.cp/ena-dis-br-range.exp.
+       * gdb.cp/ena-dis-br-range.exp: New file.
+       * gdb.cp/ena-dis-br-range.cc: New file.
+
 2017-11-06  Pedro Alves  <palves@redhat.com>
 
        * gdb.base/attach-non-pgrp-leader.c: New.
index d407408399cdc1b3a785cbda2d228f85eb8b0753..9b8d2515572b1f5cb1a5e5b1be5e1832c3ab402a 100644 (file)
@@ -324,6 +324,9 @@ set b4 [break_at main ""]
 #
 # WHAT - the command to test (disable/enable).
 #
+# Note: tests involving location ranges (and more) are found in
+# gdb.cp/ena-dis-br-range.exp.
+#
 proc test_ena_dis_br { what } {
     global b1
     global b2
diff --git a/gdb/testsuite/gdb.cp/ena-dis-br-range.cc b/gdb/testsuite/gdb.cp/ena-dis-br-range.cc
new file mode 100644 (file)
index 0000000..9469e34
--- /dev/null
@@ -0,0 +1,66 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   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/>.  */
+
+/* Some overloaded functions to test breakpoints with multiple
+   locations.  */
+
+class foo
+{
+public:
+  static void overload (void);
+  static void overload (char);
+  static void overload (int);
+  static void overload (double);
+};
+
+void
+foo::overload ()
+{
+}
+
+void
+foo::overload (char arg)
+{
+}
+
+void
+foo::overload (int arg)
+{
+}
+
+void
+foo::overload (double arg)
+{
+}
+
+void
+marker ()
+{
+}
+
+int
+main ()
+{
+  foo::overload ();
+  foo::overload (111);
+  foo::overload ('h');
+  foo::overload (3.14);
+
+  marker ();
+
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.cp/ena-dis-br-range.exp b/gdb/testsuite/gdb.cp/ena-dis-br-range.exp
new file mode 100644 (file)
index 0000000..8873c4a
--- /dev/null
@@ -0,0 +1,132 @@
+# 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/>.
+
+# This file is part of the gdb testsuite.
+
+# Test the enable/disable commands with breakpoint location ranges.
+
+# Note: more tests involving involving disable/enable commands on
+# multiple locations and breakpoints are found in
+# gdb.base/ena-dis-br.exp.
+
+if { [skip_cplus_tests] } { continue }
+
+standard_testfile .cc
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug c++}]} {
+    return -1
+}
+
+if ![runto 'marker'] then {
+    fail "run to marker"
+    return -1
+}
+
+# Returns a buffer corresponding to what GDB replies when asking for
+# 'info breakpoint'.  The parameters are all the existing breakpoints
+# enabled/disable value: 'n' or 'y'.
+
+proc make_info_breakpoint_reply_re {b1 b2 b21 b22 b23 b24} {
+    set ws "\[\t \]+"
+    return [multi_line \
+               "Num     Type${ws}Disp Enb Address${ws}What.*" \
+               "1${ws}breakpoint     keep ${b1}${ws}.* in marker\\(\\) at .*" \
+               "${ws}breakpoint already hit 1 time.*" \
+               "2${ws}breakpoint${ws}keep${ws}${b2}${ws}<MULTIPLE>.*" \
+               "2.1${ws}${b21}.*" \
+               "2.2${ws}${b22}.*" \
+               "2.3${ws}${b23}.*" \
+               "2.4${ws}${b24}.*" \
+              ]
+}
+
+gdb_test "break foo::overload" \
+    "Breakpoint \[0-9\]+ at $hex: foo::overload. .4 locations." \
+    "set breakpoint at overload"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
+    "breakpoint info"
+
+# Test the enable/disable commands, and check the enable/disable state
+# of the breakpoints/locations in the "info break" output.  CMD is the
+# actual disable/enable command. The bNN parameters are the same as
+# make_info_breakpoint_reply_re's.
+proc test_enable_disable {cmd b1 b2 b21 b22 b23 b24} {
+    gdb_test_no_output $cmd
+
+    set re [make_info_breakpoint_reply_re $b1 $b2 $b21 $b22 $b23 $b24]
+    gdb_test "info break" $re "breakpoint info $cmd"
+}
+
+# Check that we can disable/enable a breakpoint with a single
+# location.
+test_enable_disable "disable 1" n y y y y y
+test_enable_disable "enable  1" y y y y y y
+
+# Check that we can disable/disable a breakpoint with multiple
+# locations.
+test_enable_disable "disable 2" y n y y y y
+test_enable_disable "enable  2" y y y y y y
+
+# Check that we can disable/enable a single location breakpoint.
+test_enable_disable "disable 2.2" y y y n y y
+test_enable_disable "enable  2.2" y y y y y y
+
+# Check that we can disable/enable a range of breakpoint locations.
+test_enable_disable "disable 2.2-3" y y y n n y
+test_enable_disable "enable  2.2-3" y y y y y y
+
+# Check that we can disable/enable a breakpoint location range with
+# START==END.
+test_enable_disable "disable 2.2-2" y y y n y y
+test_enable_disable "enable  2.2-2" y y y y y y
+
+# Check that we can disable a location breakpoint range with max >
+# existing breakpoint location.
+gdb_test "disable 2.3-5" "Bad breakpoint location number '5'" \
+    "disable location breakpoint range with max > existing"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y n n] \
+    "breakpoint info disable 2.3 to 2.5"
+
+# Check that we can enable a location breakpoint range with max >
+# existing breakpoint location.
+gdb_test "enable 2.3-5" "Bad breakpoint location number '5'" \
+    "enable location breakpoint range with max > existing"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
+    "breakpoint info enable 2.3 to 2.5"
+
+# Check that disabling an reverse location breakpoint range does not
+# work.
+gdb_test_no_output "disable 2.3-2"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
+    "breakpoint info disable 2.3-2"
+
+# Check that disabling an invalid breakpoint location range does not
+# cause unexpected behavior.
+gdb_test "disable 2.6-7" "Bad breakpoint location number '6'" \
+    "disable an unvalid location breakpoint range"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
+    "breakpoint info disable 2.6-7"
+
+# Check that disabling an invalid breakpoint location range does not
+# cause trouble.
+gdb_test_no_output "disable 2.8-6"
+
+gdb_test "info break" [make_info_breakpoint_reply_re y y y y y y] \
+    "breakpoint info disable 2.8-6"