[v3,2/2] gdb, breakpoint: output multiple bp locations

Message ID 20240910071337.1971891-3-klaus.gerlicher@intel.com
State New
Headers
Series gdb: setting BP with multiple locations only displays |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Test failed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Patch failed to apply

Commit Message

Gerlicher, Klaus Sept. 10, 2024, 7:13 a.m. UTC
  From: "Gerlicher, Klaus" <klaus.gerlicher@intel.com>

When the user sets a breakpoint, GDB may set this breakpoint
on multiple breakpoint locations.

The user should be able to see all of them instead of just the number
of locations.

Make the number of breakpoint locations printed configurable. Introduce
the "set max-breakpoint-locations-printed" command.

Convert tests that use gdb_test "break" to using gdb_breakpoint because
previously the "break" output was parseable with a single regex. Now that
it has become multi-line, tests that were previously creating multiple
locations unknowingly need to be fixed to use the right regex which is
simpler (and more properly) done by using the gdb_breakpoint. gdb_breakpoint
knows which regex to use for any case.

This also adds two more options to gdb_breakpoint, -add_locs and -inferior:

  -add_locs is used for testing the additional print when there are more
            locations then allowed to be printed with the "set
            max-breakpoint-locations-printed" command.
  -inferior is used to match "on inferior X" output when multiple locations
            are on different inferiors.
---
 gdb/NEWS                                      |   5 +
 gdb/breakpoint.c                              | 125 ++++++++++++++----
 gdb/doc/gdb.texinfo                           |  45 ++++++-
 gdb/testsuite/gdb.base/ctxobj.exp             |   4 +-
 .../run-control-while-bg-execution.exp        |   2 +-
 gdb/testsuite/gdb.cp/ovldbreak.exp            |   4 +-
 gdb/testsuite/gdb.linespec/cpcompletion.exp   |   2 +
 gdb/testsuite/gdb.linespec/linespec.exp       |   2 +-
 gdb/testsuite/gdb.linespec/multiple-locs.cc   |  41 ++++++
 gdb/testsuite/gdb.linespec/multiple-locs.exp  |  56 ++++++++
 .../mi-breakpoint-multiple-locations.exp      |   4 +-
 .../gdb.mi/user-selected-context-sync.exp     |  16 +--
 .../gdb.multi/inferior-specific-bp.exp        |   2 +-
 .../gdb.multi/multi-target-continue.exp       |   3 +-
 .../gdb.multi/multi-target-ping-pong-next.exp |   6 +-
 gdb/testsuite/gdb.python/py-breakpoint.exp    |   2 +-
 gdb/testsuite/lib/completion-support.exp      |   3 +
 gdb/testsuite/lib/gdb.exp                     |  33 ++++-
 18 files changed, 295 insertions(+), 60 deletions(-)
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.cc
 create mode 100644 gdb/testsuite/gdb.linespec/multiple-locs.exp
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index cfc9cb05f77..4adbf0f2a66 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,11 @@ 
 
 *** Changes since GDB 15
 
+* Printing multiple breakpoint locations when setting a breakpoint
+
+  Setting a breakpoint that is instantiated for multiple locations now prints
+  locations limited by "set max-breakpoint-location-printed <count>".
+
 * GDB now supports printing of ptwrite payloads from the Intel Processor
   Trace during 'record instruction-history', 'record function-call-history'
   and all stepping commands.  The payload is also accessible in Python as a
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 7fd50ba63fc..c5c27fa6bd1 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -170,6 +170,19 @@  static bool bl_address_is_meaningful (bp_location *loc);
 
 static int find_loc_num_by_location (const bp_location *loc);
 
+/* Maximum number of locations printed when setting a breakpoint.  Controlled
+   by "set max-breakpoint-locations-printed <number>".  */
+
+static unsigned int breakpoint_show_max_locations = 10;
+static void
+show_max_breakpoint_location_printed (struct ui_file *file, int from_tty,
+		    struct cmd_list_element *c, const char *value)
+{
+  gdb_printf (file,
+	      _("Number of breakpoint location printed is %s.\n"),
+	      value);
+}
+
 /* update_global_location_list's modes of operation wrt to whether to
    insert locations now.  */
 enum ugll_insert_mode
@@ -11785,38 +11798,85 @@  code_breakpoint::say_where () const
     }
   else
     {
-      const bp_location &bl = this->first_loc ();
-      if (opts.addressprint || bl.symtab == nullptr)
-	gdb_printf (" at %ps",
-		    styled_string (address_style.style (),
-				   paddress (bl.gdbarch,
-					     bl.address)));
-      if (bl.symtab != NULL)
-	{
-	  /* If there is a single location, we can print the location
-	     more nicely.  */
-	  if (!this->has_multiple_locations ())
+      if (this->has_multiple_locations ())
+	{
+	  int n = std::distance (m_locations.begin (), m_locations.end ());
+
+	  gdb_printf (" for %s at %d locations:\n", locspec->to_string (), n);
+	  int loc_idx = 0;
+	  for (const bp_location &bl : m_locations)
+	    {
+	      gdb_printf ("    Location %u", loc_idx + 1);
+	      if (opts.addressprint || bl.symtab == nullptr)
+		gdb_printf (
+		  " at %ps",
+		  styled_string (address_style.style (),
+				 paddress (bl.gdbarch, bl.address)));
+
+	      if (bl.symtab != nullptr)
+		{
+		  const struct symbol *sym = bl.symbol;
+		  if (sym)
+		    gdb_printf (
+		      " in function %ps",
+		      styled_string (function_name_style.style (),
+				     sym->print_name ()));
+
+		  const char *filename
+		    = symtab_to_filename_for_display (bl.symtab);
+		  gdb_printf (
+		    " in file %ps, line %d",
+		    styled_string (file_name_style.style (), filename),
+		    bl.line_number);
+		}
+
+	      if (all_inferiors ().size () > 1)
+		{
+		  for (const auto &inf : all_inferiors ())
+		    if (inf->pspace == bl.pspace)
+		      gdb_printf (" on inferior %d", inf->num);
+		}
+	      else if (bl.owner->inferior != -1)
+		gdb_printf (" on inferior %d", bl.owner->inferior);
+
+	      loc_idx++;
+
+	      gdb_printf (".");
+	      if (loc_idx < n)
+		gdb_printf ("\n");
+
+	      /* The user (or default) has selected a maximum number of
+		 locations to be printed.  */
+	      if (loc_idx >= breakpoint_show_max_locations)
+		break;
+	    }
+
+	  /* We printed less locations than there are present, let the user
+	     know how many more there are.  */
+	  if (loc_idx < n)
 	    {
-	      const char *filename
-		= symtab_to_filename_for_display (bl.symtab);
-	      gdb_printf (": file %ps, line %d.",
-			  styled_string (file_name_style.style (),
-					 filename),
-			  bl.line_number);
+	      int omitted_locs = n - loc_idx;
+	      gdb_printf ("%u additional location%s not printed.",
+			  omitted_locs, omitted_locs > 1 ? "s" : "");
 	    }
-	  else
-	    /* This is not ideal, but each location may have a
-	       different file name, and this at least reflects the
-	       real situation somewhat.  */
-	    gdb_printf (": %s.", locspec->to_string ());
 	}
-
-      if (this->has_multiple_locations ())
+      else
 	{
-	  int n = std::distance (m_locations.begin (), m_locations.end ());
-	  gdb_printf (" (%d locations)", n);
+	  const bp_location &bl = this->first_loc ();
+	  if (opts.addressprint || bl.symtab == nullptr)
+	    gdb_printf (" at %ps",
+			styled_string (address_style.style (),
+				       paddress (bl.gdbarch, bl.address)));
+	  if (bl.symtab != nullptr)
+	    {
+		const char *filename
+		  = symtab_to_filename_for_display (bl.symtab);
+		gdb_printf (": file %ps, line %d.",
+			    styled_string (file_name_style.style (), filename),
+			    bl.line_number);
+	    }
 	}
-    }
+      }
 }
 
 /* See breakpoint.h.  */
@@ -15244,6 +15304,17 @@  Usage: agent-printf \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
 This supports most C printf format specifications, like %s, %d, etc.\n\
 This is useful for formatted output in user-defined commands."));
 
+  add_setshow_uinteger_cmd ("max-breakpoint-locations-printed", class_support,
+			   &breakpoint_show_max_locations,_("\
+Set number of locations printed when setting a breakpoint."), _("\
+Show number of locations printed when setting a breakpoint."), _("\
+Use this to choose how many number of locations are printed\n\
+when setting a breakpoint.\n\
+A value of \"unlimited\", or zero, means there's no limit."),
+			   nullptr, /* set_cmd */
+			   show_max_breakpoint_location_printed,
+			   &setlist, &showlist);
+
   automatic_hardware_breakpoints = true;
 
   gdb::observers::about_to_proceed.attach (breakpoint_about_to_proceed,
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 780eaf41f16..c747de45e12 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -4518,16 +4518,47 @@  $1 = 1
 
 A breakpoint may be mapped to multiple code locations for example with
 inlined functions, Ada generics, C@t{++} templates or overloaded function names.
-@value{GDBN} then indicates the number of code locations in the breakpoint
+@value{GDBN} then lists the number of code locations in the breakpoint
 command output:
 @smallexample
 (gdb) b some_func
-Breakpoint 2 at 0x1179: some_func. (3 locations)
+Breakpoint 2 for some_func at 3 locations:
+    Location 1 at 0x118e in function some_func in file somefunc.cc, line 23.
+    Location 2 at 0x11ee in function some_func in file somefunc.cc, line 38.
+    Location 3 at 0x12fe in function some_func in file someother.cc, line 58.
 (gdb) p $bpnum
 $2 = 2
 (gdb)
 @end smallexample
 
+@cindex @code{break}, how many locations are displayed
+By default, @value{GDBN} prints up to ten breakpoint locations, you can change
+this using @code{set max-breakpoint-locations-printed}:
+
+@smallexample
+(gdb) set max-breakpoint-locations-printed 2
+(gdb) b some_func
+Breakpoint 2 for some_func at 3 locations:
+    Location 1 at 0x118e in function some_func in file somefunc.cc, line 23.
+    Location 2 at 0x11ee in function some_func in file somefunc.cc, line 38.
+1 additional location not printed.
+(gdb)
+@end smallexample
+
+@table @code
+@kindex set max-breakpoint-locations-printed
+@item set max-breakpoint-locations-printed @var{count}
+@itemx set max-breakpoint-locations-printed unlimited
+Make the @code{break} command display up to @var{count} locations.
+Setting @var{count} to @code{unlimited} or 0 means there's no limit.
+
+@kindex show max-breakpoint-locations-printed
+@item show max-breakpoint-locations-printed
+Display the number of locations that @code{break} will print.  If the number
+of locations is greater than the limit, the additional number of locations
+will be listed.
+@end table
+
 @vindex $_hit_bpnum@r{, convenience variable}
 @vindex $_hit_locno@r{, convenience variable}
 When your program stops on a breakpoint, the convenience variables
@@ -4637,7 +4668,10 @@  warning: failed to validate condition at location 0x11ce, disabling:
   No symbol "a" in current context.
 warning: failed to validate condition at location 0x11b6, disabling:
   No symbol "a" in current context.
-Breakpoint 1 at 0x11b6: func. (3 locations)
+Breakpoint 1 for func at 3 locations:
+    Location 1 at 0x11ce in function func in file somefunc.cc, line 23.
+    Location 2 at 0x11b6 in function func in file somefunc.cc, line 38.
+    Location 3 at 0x12fe in function func in file someother.cc, line 58.
 @end smallexample
 
 Locations that are disabled because of the condition are denoted by an
@@ -4681,7 +4715,10 @@  warning: failed to validate condition at location 2, disabling:
   No symbol "foo" in current context.
 warning: failed to validate condition at location 3, disabling:
   No symbol "foo" in current context.
-Breakpoint 1 at 0x1158: test.c:18. (3 locations)
+Breakpoint 1 for func at 3 locations:
+    Location 1 at 0x1158 in function func in file test.c, line 18.
+    Location 2 at 0x11b6 in function func in file test.c, line 28.
+    Location 3 at 0x12fe in function func in file test.c, line 58.
 @end smallexample
 
 This causes all the present locations where the breakpoint would
diff --git a/gdb/testsuite/gdb.base/ctxobj.exp b/gdb/testsuite/gdb.base/ctxobj.exp
index d1137189108..e73b29fcf3a 100644
--- a/gdb/testsuite/gdb.base/ctxobj.exp
+++ b/gdb/testsuite/gdb.base/ctxobj.exp
@@ -61,9 +61,7 @@  if ![runto_main] {
 }
 
 set bp_location [gdb_get_line_number "STOP" "ctxobj-f.c"]
-gdb_test "break ctxobj-f.c:$bp_location" \
-         "Breakpoint \[0-9\]+ at 0x\[0-9a-fA-F\]+: .*" \
-         "break in get_version functions"
+gdb_breakpoint "ctxobj-f.c:$bp_location"
 
 global expect_out
 set test "continue to get_version_1"
diff --git a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
index 380047ae854..fd395f10387 100644
--- a/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
+++ b/gdb/testsuite/gdb.base/run-control-while-bg-execution.exp
@@ -90,7 +90,7 @@  proc do_test { action1 action2 } {
     if { $action2 == "start" } {
 	gdb_test "start" "Temporary breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
     } elseif { $action2 == "run" } {
-	gdb_test "break main" "Breakpoint $::decimal at $::hex.*"
+	gdb_breakpoint "main"
 	gdb_test "run" "Breakpoint $::decimal\(?:\.$::decimal\)?, main .*"
     } elseif { $action2 == "attach" } {
 	set test_spawn_id [spawn_wait_for_attach $::binfile]
diff --git a/gdb/testsuite/gdb.cp/ovldbreak.exp b/gdb/testsuite/gdb.cp/ovldbreak.exp
index 84efb37b8db..573b47f0423 100644
--- a/gdb/testsuite/gdb.cp/ovldbreak.exp
+++ b/gdb/testsuite/gdb.cp/ovldbreak.exp
@@ -332,6 +332,8 @@  gdb_test "info breakpoints" "No breakpoints, watchpoints, tracepoints, or catchp
 # Test choice "all".
 # This is copy-and-paste from set_bp_overloaded.
 
+gdb_test_no_output "set max-breakpoint-locations-printed 0"
+
 incr bpnum
 send_gdb "break foo::overload1arg\n" 
 gdb_expect {
@@ -340,7 +342,7 @@  gdb_expect {
         # Choose all.
         send_gdb "1\n"
         gdb_expect {
-	    -re "Breakpoint $bpnum at $hex: foo::overload1arg. .12 locations.\r\n.*$gdb_prompt $" {
+	    -re "[gdb_multi_loc_regex Breakpoint 12]$gdb_prompt $" {
                 pass "set bp on overload1arg all"
             }
             -re ".*$gdb_prompt $" {
diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp
index 480e03406f2..81e56f4b6ac 100644
--- a/gdb/testsuite/gdb.linespec/cpcompletion.exp
+++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp
@@ -1397,4 +1397,6 @@  proc test_driver {} {
     if-expression
 }
 
+gdb_test_no_output "set max-breakpoint-locations-printed 0"
+
 test_driver
diff --git a/gdb/testsuite/gdb.linespec/linespec.exp b/gdb/testsuite/gdb.linespec/linespec.exp
index 981cd4155c0..e99a6ddaa46 100644
--- a/gdb/testsuite/gdb.linespec/linespec.exp
+++ b/gdb/testsuite/gdb.linespec/linespec.exp
@@ -69,7 +69,7 @@  if { [is_remote host] } {
 
 		send_gdb "\n"
 		gdb_test "" \
-		    "Breakpoint $decimal at $hex: thefile.cc:twodup\\(\\). \[(\]2 locations\[)\]" \
+		    "[gdb_multi_loc_regex Breakpoint 2]\[^\r\n\]*" \
 		    "set break at unique function name in two source files"
 	    }
 	}
diff --git a/gdb/testsuite/gdb.linespec/multiple-locs.cc b/gdb/testsuite/gdb.linespec/multiple-locs.cc
new file mode 100644
index 00000000000..e7ca889e0fa
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/multiple-locs.cc
@@ -0,0 +1,41 @@ 
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2024 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/>.  */
+
+template <typename T>
+int
+templ1 (T param)
+{
+  return (int) param; /* Break here.  */
+}
+
+template <>
+int
+templ1 (char* param)
+{
+  return  2;
+}
+
+int
+main ()
+{
+  templ1 (1L);
+  templ1 (1.0f);
+  templ1 (1U);
+  templ1 (1UL);
+  templ1 (1ULL);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.linespec/multiple-locs.exp b/gdb/testsuite/gdb.linespec/multiple-locs.exp
new file mode 100644
index 00000000000..5dc3ee5ff8a
--- /dev/null
+++ b/gdb/testsuite/gdb.linespec/multiple-locs.exp
@@ -0,0 +1,56 @@ 
+# Copyright 2024 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/>.
+
+# Tests of printing of multiple breakpoint locations when setting a breakpoint.
+
+standard_testfile .cc
+
+require allow_cplus_tests
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile]} {
+    return -1
+}
+
+gdb_test_no_output "set confirm off"
+
+gdb_test_no_output "set multiple-symbols all"
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+gdb_breakpoint "$bp_location" -locs 5
+
+delete_breakpoints
+
+gdb_test_no_output "set max-breakpoint-locations-printed 2"
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+
+gdb_breakpoint $bp_location -locs 5 -add_locs 3
+
+delete_breakpoints
+
+set bp_location [gdb_get_line_number "Break here." $srcfile]
+
+gdb_breakpoint "$bp_location inferior 1" -locs 5 -add_locs 3 -inferior 1
+
+delete_breakpoints
+
+# Now add the 2nd inferior.
+gdb_test "add-inferior" "Added inferior 2.*" "add inferior 2"
+gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2"
+gdb_file_cmd $binfile
+
+gdb_test_no_output "set max-breakpoint-locations-printed 20"
+
+gdb_breakpoint "$bp_location" -locs 10 -inferior "\[12\]"
diff --git a/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp b/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
index 93ddad84444..a02fe1f7cce 100644
--- a/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
+++ b/gdb/testsuite/gdb.mi/mi-breakpoint-multiple-locations.exp
@@ -69,7 +69,9 @@  proc do_test { mi_version use_fix_flag expect_fixed_output } {
 	    set pattern [make_breakpoints_pattern $expect_fixed_output 2 y y]
 	    mi_gdb_test "break a_very_unique_name" \
 		[multi_line "&\"break a_very_unique_name\\\\n\"" \
-			    "~\"Breakpoint ${decimal} at.*\\(2 locations\\)\\\\n\"" \
+			    "~\"Breakpoint ${decimal} for.*at.*2 locations:\\\\n\"" \
+			    "~\"    Location ${decimal}.*\\\\n\"" \
+			    "~\"    Location ${decimal}.*\\\\n\"" \
 			    "=breakpoint-created,${pattern}" \
 			    "\\^done" ] \
 		"break a_very_unique_name"
diff --git a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
index e168a5eee45..b8d640d010a 100644
--- a/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
+++ b/gdb/testsuite/gdb.mi/user-selected-context-sync.exp
@@ -293,9 +293,7 @@  proc test_continue_to_start { mode inf } {
 	with_spawn_id $gdb_main_spawn_id {
 	    # Continue to the point where we know for sure the threads are
 	    # started.
-	    gdb_test "tbreak $srcfile:$main_break_line" \
-		"Temporary breakpoint ${any}" \
-		"set breakpoint in main"
+	    gdb_breakpoint "$srcfile:$main_break_line" {temporary}
 
 	    gdb_continue_to_breakpoint "main breakpoint"
 
@@ -321,9 +319,7 @@  proc test_continue_to_start { mode inf } {
 		foreach thread { 2 3 } {
 		    gdb_test "thread $inf.$thread" ".*" "select child thread $inf.$thread"
 
-		    gdb_test "tbreak $srcfile:$thread_loop_line" \
-			"Temporary breakpoint ${any}" \
-			"set breakpoint for thread $inf.$thread"
+		    gdb_breakpoint "$srcfile:$thread_loop_line" {temporary}
 
 		    gdb_continue_to_breakpoint "continue thread $inf.$thread to infinite loop breakpoint"
 
@@ -347,12 +343,8 @@  proc test_continue_to_start { mode inf } {
 		# Put a thread-specific breakpoint for thread 2 of the current
 		# inferior.  We don't put a breakpoint for thread 3, since we
 		# want to let it run.
-		set test "set thread-specific breakpoint, thread $inf.2"
-		gdb_test_multiple "tbreak $srcfile:$thread_loop_line thread $inf.2" $test {
-		    -re "Temporary breakpoint ${any}\r\n$gdb_prompt " {
-			pass $test
-		    }
-		}
+		gdb_breakpoint "$srcfile:$thread_loop_line thread $inf.2" \
+		    {temporary}
 
 		# Confirm the stop of thread $inf.2.
 		set test "thread $inf.2 stops CLI"
diff --git a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
index 28d0d90d056..0103b8985d3 100644
--- a/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
+++ b/gdb/testsuite/gdb.multi/inferior-specific-bp.exp
@@ -105,7 +105,7 @@  proc check_info_breakpoints { testname bp_number expected_loc_count } {
 # Create an inferior-specific breakpoint.  Use gdb_test instead of
 # gdb_breakpoint here as we want to check the breakpoint was placed in
 # multiple locations.
-gdb_breakpoint "foo inferior 1" -locs 2
+gdb_breakpoint "foo inferior 1" -locs 2 -inferior 1
 set bp_number [get_integer_valueof "\$bpnum" "INVALID" \
 		  "get b/p number for inferior specific breakpoint"]
 
diff --git a/gdb/testsuite/gdb.multi/multi-target-continue.exp b/gdb/testsuite/gdb.multi/multi-target-continue.exp
index d4b2fc28133..2e8dcae13bf 100644
--- a/gdb/testsuite/gdb.multi/multi-target-continue.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-continue.exp
@@ -29,8 +29,7 @@  proc test_continue {non-stop} {
     }
 
     proc set_break {inf} {
-	gdb_test "break function${inf} thread ${inf}.1" \
-	    "Breakpoint ${::decimal} at ${::hex}: file .*, line ${::decimal}\\."
+	gdb_breakpoint "function${inf} thread ${inf}.1"
     }
 
     # Select inferior INF, and then run to a breakpoint on inferior
diff --git a/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp b/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
index 36f9d24a917..fe2683e9edb 100644
--- a/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
+++ b/gdb/testsuite/gdb.multi/multi-target-ping-pong-next.exp
@@ -51,13 +51,11 @@  proc test_ping_pong_next {} {
 
     gdb_test "thread 1.1" "Switching to thread 1.1 .*"
 
-    gdb_test "break $srcfile:$line1 thread 1.1" \
-	"Breakpoint .*$srcfile, line $line1\\."
+    gdb_breakpoint "$srcfile:$line1 thread 1.1"
 
     gdb_test "continue" "hit Breakpoint .*"
 
-    gdb_test "break $srcfile:$line2 thread 2.1" \
-	"Breakpoint .*$srcfile, line $line2\\."
+    gdb_breakpoint "$srcfile:$line2 thread 2.1"
 
     # Now block inferior 1 and issue "next".  We should stop at the
     # breakpoint for inferior 2, given schedlock off.
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 934690db2a1..210a3d35160 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -765,7 +765,7 @@  proc_with_prefix test_bkpt_qualified {} {
     clean_restart ${testfile}
 
     set one_location_re "Breakpoint $decimal at $hex:.*line $decimal."
-    set two_location_re "Breakpoint $decimal at $hex:.*2 locations."
+    set two_location_re "[gdb_multi_loc_regex Breakpoint 2]\[^\r\n\]*"
 
     if {![runto_main]} {
 	return 0
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
index 5f0f6199894..92207c350e0 100644
--- a/gdb/testsuite/lib/completion-support.exp
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -417,6 +417,9 @@  proc completion::_create_bp {break_command} {
 	-re "\\\(\($decimal\) locations\\\)\r\n$gdb_prompt $" {
 	    set found_locations "$expect_out(1,string)"
 	}
+	-re "[gdb_multi_loc_regex Breakpoint \[0-9\]+]$gdb_prompt $" {
+	    set found_locations 1
+	}
 	-re "Breakpoint $decimal at $hex: file .*, line .*$gdb_prompt $" {
 	    set found_locations 1
 	}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 7309937bf19..d310911449b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -668,15 +668,30 @@  proc gdb_starti_cmd { {inferior_args {}} } {
     return -1
 }
 
+# Construct a breakpoint regex for multiple bp locations given a breakpoint
+# command BREAK_MESSAGE and a regex of expected locations LOCS.
+# This is useful for constructing multiple location regex where the
+# gdb_breakpoint proc cannot be used (for instance in completion tests).
+# It is also used in proc gdb_breakpoint.
+
+proc gdb_multi_loc_regex { break_message locs { inferior ""}} {
+    return [multi_line \
+	"$break_message \[0-9\]+ for \[^\r\n\]+ at\[^\r\n\]* $locs locations:" \
+	"(?:    Location $::decimal at $::hex\[^\r\n\]*$inferior\.\\r\n)+" ]
+}
+
 # Set a breakpoint using LINESPEC.
 #
 # If there is an additional argument it is a list of options; the supported
 # options are allow-pending, temporary, message, no-message and qualified.
 # For multiple breakpoint locations additional options are used. These
-# are -locs and -extra:
+# are -locs, -extra, -add_locs and -inferior:
 # -locs specifies how many locations to expect the breakpoint set output
 #       and enforces that a single location match is an error.
 # -extra allows specifying extra matches that need to appear in the output.
+# -add_locs specifies a regex for  additional locations printed.
+# -inferior specifies a regex for "on inferior" output locations are in
+#           different inferiors.
 #
 # The result is 1 for success, 0 for failure.
 #
@@ -692,6 +707,7 @@  proc gdb_breakpoint { linespec args } {
 
     parse_args {
 	{locs "\[0-9\]+"}
+	{add_locs ""}
 	{extra "<nomatch>"}
 	{inferior ""}
     }
@@ -734,6 +750,19 @@  proc gdb_breakpoint { linespec args } {
 
     set test_name "gdb_breakpoint: set breakpoint at $linespec"
 
+    # If reqeusted, expect "on inferior" in the locations output.
+    if { $inferior != ""} {
+	set inferior "on inferior $inferior"
+    }
+
+    set multiloc_msg [gdb_multi_loc_regex $break_message $locs $inferior]
+
+    set additional_locs ""
+    if { $add_locs != "" } {
+	set additional_locs \
+	    "$add_locs additional location[s]* not printed\.\r\n"
+    }
+
     # The extra regex is setup to not match unless the caller specifies
     # an extra match.
     gdb_test_multiple "$break_command $linespec" $test_name {
@@ -747,7 +776,7 @@  proc gdb_breakpoint { linespec args } {
 	-re "$break_message \[0-9\]*: file .*, line $decimal.\r\n$gdb_prompt $" {
 	    incr single_loc
 	}
-	-re "$break_message \[0-9\]+ at .*\($locs locations\).*\r\n$gdb_prompt $" {
+	-re "$multiloc_msg$additional_locs$gdb_prompt $" {
 	    incr multiple_locs
 	}
 	-re "$break_message \[0-9\]+ at .*\.\r\n$gdb_prompt $" {